У меня есть код, который я borrowed from Steve Marx. Основной блок используется в потоке роли рабочего лазурного дерева, чтобы взять арендную плату на лазурном блобе. Это обеспечивает механизм блокировки для синхронизации по нескольким рабочим экземплярам, когда требуется только один экземпляр для обработки задания за раз. Однако, поскольку у вас могут быть рабочие места, для выполнения которых потребуется больше времени, чем тайм-аут аренды blob, создается новый поток, который обновляет сделку blob так часто.Это подходящее место для вызова Thread.Abort()?
Эта нить обновления спит и обновляется бесконечным циклом. Когда основной поток выходит (через Dispose
у потребителя класса), renewalThread.Abort()
is invoked. Это приводит к тому, что все виды ThreadAbortException
s будут брошены в рабочую роль.
Мне интересно, это лучший способ справиться с этим? Что мне не нравится в этом, так это то, что у вас может быть несколько потоков обновления, которые остаются спящими после того, как потребитель, который их породил, был удален. Что-то плохое в коде ниже? Если это так, есть ли лучший способ? Или 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;
}
}
Update
Допустим, у вас есть лазурь роль работник развернутый с 2 или более экземпляров. Давайте также скажем, что у вас есть работа, которую оба экземпляра могут обрабатывать для вас. В ролях рабочих 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 секунд. Существует встроенный тайм-аут на лизинг blob, поэтому, если вы хотите, чтобы другая роль блокировалась до завершения процесса, вам необходимо периодически продлевать эту арендную плату, чтобы сохранить ее в тайм-ауте. Это то, что описанный выше класс заключает в себе - автоматическое продление аренды до тех пор, пока вы не избавитесь от него как потребителя.
Мой вопрос в основном о тайм-ауте сна в обновленииThread. Произнесите задание за 2 секунды. ОбновлениеThread изящно выйдет (я думаю), но не на другие 38 секунд. Вот где лежит мясо неопределенности в моем вопросе. Исходный код вызвал updateThread.Abort(), который заставил его немедленно прекратить действие. Лучше ли это сделать, или позволить ему поспать и выйти изящно в более позднее время? Если вы ежедневно принимаете метод Run
роли, вы можете получить до 40 из этих возобновляемых потоков, ожидающих выхода из строя. Если у вас разные блокировки заданий на разных блоках, это число умножается на количество сфотографированных блоков. Однако, если вы делаете это с помощью Thread.Abort(), вы получаете столько же потоков ThreadAbortExceptions, которые стекаются в стеке.
установка 'isRenewing' на false приведет к тому, что поток выйдет изящно правильно? Я что-то упускаю? Я не уверен в методе «RenewLease», хотя –
. Я обновлю вопрос, чтобы предоставить дополнительную информацию. – danludwig
Нет подходящих мест для вызова Thread.Abort(). –