2011-01-14 47 views
2

我有一個由工作項集合組成的作業對象。每個作業都有自己的與之相關的WatcherClass,它經常檢查數據庫以查看是否需要取消執行。它可以在工作流程的任何迭代中被取消。如果它被取消,則從foreach塊運行的任何線程都會傳播取消並正常退出。使用相同的鎖修改鎖內的對象

我的監視器代碼中是否有任何問題可能導致死鎖?我試圖只允許一個線程處理定時器回調通過使用Timer.Change(Timeout.Infinite,Timeout.Infinite),但事實上,我改變WatcherClass.Job裏面的鎖定聲明擊敗鎖的目的(因爲我包裹同一個鎖對象中的_Job的get/set)?該代碼似乎工作正常,但我知道這並不是什麼跡象。

在主線程的代碼看起來類同此:

using (WatcherClass watcher = new WatcherClass()) 
{ 
    watcher.CancelTokenSource = new CancellationTokenSource(); 
    watcher.Start(); 
    foreach (SomeJob job in worksflow.Jobs) 
    { 
     watcher.Job = job; 
     //Do some stuff async 
     //Do some more stuff async 
    } 

} 

public class WatcherClass : IDisposable 
{ 
    private System.Threading.Timer _WatcherTimer; 
    private readonly object locker = new object(); 
    private bool _Disposed = false; 
    private SomeJob _Job; 

    public SomeJob Job 
    { 
     get 
     { 
      lock (locker) 
      { 
       return _Job; 
      } 
     } 
     set 
     { 
      lock (locker) 
      { 
       _Job= value; 
      } 
     } 
    } 

    public System.Threading.Task.CancellationTokenSource 
     CancelToken { get; set; } 

    public WatcherClass() 
    { 
     _WatcherTimer = new Timer(new TimerCallback(DoCheck), null, 
      Timeout.Infinite, Timeout.Infinite); 
    } 

    public void Start() 
    { 
     _WatcherTimer.Change(30000, Timeout.Infinite); 
    } 

    public void DoCheck(object state) 
    { 

     lock (locker) 
     { 

      if (_Disposed || this.CancelToken.IsCancellationRequested) 
       return; 

      _WatcherTimer.Change(Timeout.Infinite, Timeout.Infinite); 

      //Check database to see if task is cancelled 
      if (cancelled) 
      { 
       this.CancelToken.Cancel(); 
       _Job.CancelResult = CancelResult.CanceledByUser; 
       _Job.SomeOtherProperty = true; 
      } 
      else 
      { 
       //Safe to continue 
       _WatcherTimer.Change(30000, Timeout.Infinite); 
      } 
     } 

    } 

    public void Dispose(bool disposing) 
    { 
     lock (locker) 
     { 
      if (disposing) 
      { 
       if (_WatcherTimer != null) 
        _WatcherTimer.Dispose(); 

      } 
      _Disposed = true; 
     } 
    } 
} 
+0

要清楚,這些是你的`類任務`,`類CancelToken`,而不是來自TPL的? – 2011-01-14 19:31:01

+0

@亨克:CancelToken來自TPL,任務不是 - 任務就是我爲這個例子所調用的。謝謝 – 2011-01-14 19:35:56

回答

1

你周圍的工作特性,並在DoCheck功能AQUIRE鎖只保護訪問WatcherClass的內部_Task領域。在DoCheck中,你也在修改_task對象本身的屬性。鎖不會阻止其他人同時從其他線程修改任務對象的字段。

如果在您的應用程序中,任務對象只能由DoCheck操作,那麼您可能沒問題。如果任務對象可能被DoCheck以外的代碼操縱,那麼你可能會遇到問題。

另外請記住,您創建的每個額外的鎖都是一個額外的死鎖機會。如果多個鎖始終按特定順序獲取,則它們可能無死鎖。如果代碼流允許在某些情況下在鎖B之前獲取鎖A,或在其他情況下鎖A之前鎖B,則存在嚴重的死鎖風險。 (線程1鎖定A,嘗試在線程2鎖定B並嘗試鎖定A =>死鎖時鎖定B)

在您的WatcherClass情況下,如果要使用自己的鎖具有多個watcherclass實例,請小心不要讓外部調用(或火災事件)最終嘗試獲取其他監視器實例中的鎖。這是一個等待發生的AB/BA僵局。