我有一些从 Steve Marx 那里借来的代码。主块在 azure worker 角色线程中用于获取 azure blob 的租约。当您只希望一个实例一次处理一个作业时,这提供了一种锁定机制,用于在多个工作实例之间进行同步。但是,由于您的作业可能需要比 blob 租约超时时间更长的时间才能完成,因此会产生一个新线程来每隔一段时间更新 blob 租约。
此更新线程在无限循环中休眠和更新。当主线程退出时(通过Dispose
类的消费者),renewalThread.Abort()
被调用。ThreadAbortException
这导致在worker角色中抛出各种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;
}
}
更新
假设您有一个部署了 2 个或更多实例的 azure worker 角色。还假设您有一份工作,两个实例都有能力为您处理。在工作角色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);
}
}
每一秒,该角色都会检查某些作业是否计划运行,如果是,则处理它们。但是,为了避免让两个角色实例处理相同的作业,您首先要对 blob 进行租约。通过这样做,另一个实例无法访问该 blob,因此它被有效地阻止,直到第一个实例完成处理。(注意:在上面的 .Perform 方法中发生新的租约。)
现在,假设一项工作可能需要 1 到 100 秒才能完成。Blob 租约有一个内置的超时,因此如果您想在进程完成之前阻止其他角色,您必须定期更新该租约,以使其保持超时。这就是上面的类所封装的——自动更新租约,直到您将其作为消费者处理。
我的问题主要是关于renewThread中的睡眠超时。说工作在 2 秒内完成。更新线程将优雅地退出(我认为),但不会再过 38 秒。这就是我的问题的不确定性所在。原始代码调用了renewThread.Abort(),导致它立即停止。这样做更好,还是让它休眠并在以后优雅地退出?如果您Run
每秒一次对角色的方法进行心跳检测,则最多可能有 40 个此类更新线程等待正常退出。如果您有不同的作业阻塞在不同的 blob 上,则该数字将乘以租用的 blob 数。但是,如果您使用 Thread.Abort() 执行此操作,您会在堆栈上得到同样多的 ThreadAbortExceptions。