У меня есть код, который я позаимствовал у Стива Маркса. Основной блок используется в потоке рабочей роли Azure для получения аренды на лазурный большой двоичный объект. Это обеспечивает механизм блокировки для синхронизации нескольких рабочих экземпляров, когда вы хотите, чтобы только один экземпляр обрабатывал задание за раз. Однако, поскольку у вас могут быть задания, выполнение которых займет больше времени, чем тайм-аут аренды большого двоичного объекта, периодически создается новый поток для обновления аренды большого двоичного объекта.
Этот поток обновления спит и обновляется в бесконечном цикле. Когда основной поток завершается (через Dispose
в классе-потребителе), renewalThread.Abort()
становится вызывается. Это приводит к тому, что в рабочую роль бросаются все виды ThreadAbortException
.
Мне интересно, это лучший способ справиться с этим? Что мне не нравится в этом, так это то, что у вас может быть несколько потоков обновления, которые остаются спящими после того, как потребитель, который их породил, был удален. Есть ли что-нибудь плохое в приведенном ниже коде? Если да, есть ли способ лучше? Или здесь Thread.Abort()
уместно?
public class AutoRenewLease : IDisposable
{
private readonly CloudBlockBlob _blob;
public readonly string LeaseId;
private Thread _renewalThread;
private volatile bool _isRenewing = true;
private bool _disposed;
public bool HasLease { get { return LeaseId != null; } }
public AutoRenewLease(CloudBlockBlob blob)
{
_blob = blob;
// acquire lease
LeaseId = blob.TryAcquireLease(TimeSpan.FromSeconds(60));
if (!HasLease) return;
// keep renewing lease
_renewalThread = new Thread(() =>
{
try
{
while (_isRenewing)
{
Thread.Sleep(TimeSpan.FromSeconds(40.0));
if (_isRenewing)
blob.RenewLease(AccessCondition
.GenerateLeaseCondition(LeaseId));
}
}
catch { }
});
_renewalThread.Start();
}
~AutoRenewLease()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed) return;
if (disposing && _renewalThread != null)
{
//_renewalThread.Abort();
_isRenewing = false;
_blob.ReleaseLease(AccessCondition
.GenerateLeaseCondition(LeaseId));
_renewalThread = null;
}
_disposed = true;
}
}
Обновить
Допустим, у вас развернута рабочая роль Azure с двумя или более экземплярами. Предположим также, что у вас есть работа, которую оба экземпляра могут выполнить за вас. При использовании метода рабочих ролей Run
у вас может получиться что-то вроде этого:
public override void Run()
{
while (true)
{
foreach (var task in _workforce)
{
var job = task.Key;
var workers = task.Value;
foreach (var worker in workers)
worker.Perform((dynamic)job);
}
Thread.Sleep(1000);
}
}
Каждую секунду роль будет проверять, запланировано ли выполнение определенных заданий, и, если да, обрабатывать их. Однако, чтобы избежать того, чтобы оба экземпляра роли выполняли одно и то же задание, сначала нужно арендовать большой двоичный объект. При этом другой экземпляр не может получить доступ к большому двоичному объекту, поэтому он эффективно блокируется до тех пор, пока первый экземпляр не завершит обработку. (Примечание: получение новой аренды происходит в рамках метода .Perform выше.)
Теперь предположим, что выполнение задания может занять от 1 до 100 секунд. В аренде больших двоичных объектов есть встроенный тайм-аут, поэтому, если вы хотите, чтобы другая роль была заблокирована до завершения процесса, вам нужно периодически продлевать эту аренду, чтобы она не превышала тайм-аут. Это то, что инкапсулирует приведенный выше класс - автоматическое продление аренды до тех пор, пока вы не избавитесь от нее в качестве потребителя.
Мой вопрос в основном касается тайм-аута сна в RenewalThread. Скажите, что работа завершена за 2 секунды. ОбновлениеThread изящно завершится (я думаю), но не в течение следующих 38 секунд. Вот в чем суть неуверенности в моем вопросе. Исходный код вызвал RenewalThread.Abort (), что привело к немедленному прекращению его работы. Что лучше сделать, или дать ему уснуть и изящно выйти позже? Если вы запускаете Run
метод роли одновременно в секунду, у вас может быть до 40 таких потоков обновления, ожидающих корректного завершения. Если у вас есть разные задания, блокирующие разные BLOB-объекты, это число умножается на количество арендованных BLOB-объектов. Однако, если вы сделаете это с помощью Thread.Abort (), вы получите столько же исключений ThreadAbortExceptions в стеке.
isRenewing
на false приведет к корректному завершению потока, верно? Я что-то упускаю? Я не уверен насчетRenewLease
метода, хотя - person Sriram Sakthivel   schedule 25.12.2013