2013-12-25 3 views
0

У меня есть код, который я 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, которые стекаются в стеке.

+0

установка 'isRenewing' на false приведет к тому, что поток выйдет изящно правильно? Я что-то упускаю? Я не уверен в методе «RenewLease», хотя –

+0

. Я обновлю вопрос, чтобы предоставить дополнительную информацию. – danludwig

+1

Нет подходящих мест для вызова Thread.Abort(). –

ответ

2

Как я понимаю, у вас есть работа, требующая аренды какого-либо объекта. Срок аренды может истечь, поэтому вы хотите, чтобы что-то постоянно возобновляло аренду до тех пор, пока работа выполняется.

Вам не нужна нить в контуре сна. Вам нужен таймер. Например:

public class AutoRenewLease : IDisposable 
{ 
    private readonly CloudBlockBlob _blob; 
    public readonly string LeaseId; 
    private System.Threading.Timer _renewalTimer; 
    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; 

     _renewalTimer = new System.Threading.Timer(x => 
     { 
      if (_isRenewing) 
      { 
       blob.RenewLease(AccessCondition 
        .GenerateLeaseCondition(LeaseId)); 
      } 
     }, null, TimeSpan.FromSeconds(40), TimeSpan.FromSeconds(40)); 


    ~AutoRenewLease() 
    { 
     Dispose(false); 
    } 

    public void Dispose() 
    { 
     Dispose(true); 
     GC.SuppressFinalize(this); 
    } 

    protected virtual void Dispose(bool disposing) 
    { 
     if (_disposed) return; 
     if (disposing && _renewalTimer != null) 
     { 
      _isRenewing = false; 
      _renewalTimer.Dispose(); 
      _blob.ReleaseLease(AccessCondition 
       .GenerateLeaseCondition(LeaseId)); 
      _renewalTimer = null; 
     } 
     _disposed = true; 
    } 
} 

Там нет необходимости тратить ресурсы, которые использует нить, чтобы убедиться, что он может спать большую часть времени. Использование таймера исключает опрос, а также устраняет необходимость в Thread.Abort.

+0

Итак, в этом случае '_isRenewing' не нужно помечать как' volatile', правильно? Так как от него больше не читается вторая нить? – danludwig

+0

'_isRenewing' не должно быть' volatile'. В идеале вы должны использовать событие или маркер отмены, а не флаг 'Boolean', но в этом случае флаг будет работать нормально. –

1

Abort следует избегать, когда это возможно. Есть места, в которых вы действительно нуждаетесь, но для этого сценария я думаю, что мы можем сделать это лучше, не прерывая.

Сделать это простым с помощью ManualResetEvent. Это остановит вашу нить изящно и немедленно без использования Abort.

private ManualResetEvent jobSignal = new ManualResetEvent(false); 
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) 
      { 
       if(jobSignal.WaitOne(TimeSpan.FromSeconds(40.0))) 
       { 
        //Disposed so stop working 
        jobSignal.Dispose(); 
        jobSignal = null; 
        return; 
       } 
       if (_isRenewing) 
        blob.RenewLease(AccessCondition 
         .GenerateLeaseCondition(LeaseId)); 
      } 
     } 
     catch (Exception ex) {//atleast log it } 
    }); 
    _renewalThread.Start(); 
} 

protected virtual void Dispose(bool disposing) 
{ 
    if (_disposed) return; 
    if (disposing && _renewalThread != null) 
    { 
     jobSignal.Set();//Signal the thread to stop working 
     _isRenewing = false; 
     _blob.ReleaseLease(AccessCondition 
      .GenerateLeaseCondition(LeaseId)); 
     _renewalThread = null; 
    } 
    _disposed = true; 
} 

Надеюсь, это поможет.

Смежные вопросы