2011-01-14 48 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僵局。