我有一个由一组工作项组成的工作对象。每个作业都有自己的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 :(得分:1)
您在Task属性和DoCheck函数中获取的锁仅保护对WatcherClass的内部_task字段的访问。在DoCheck中,您还要修改_task对象本身的属性。锁定不会阻止其他任何人同时从其他线程修改任务对象的字段。
如果在您的应用程序中任务对象仅由DoCheck操纵,那么您可能没问题。如果任务对象可能被DoCheck以外的代码操纵,那么您可能会遇到问题。
另请注意,您创建的每个额外锁定都是死锁的额外机会。如果始终以特定顺序获取多个锁,则它们可以无死锁。如果代码流允许在某些情况下在锁B之前获取锁A,或者在其他情况下在锁A之前锁定B,那么您将面临严重的死锁风险。 (线程1锁定A,尝试锁定B,而线程2锁定B并尝试锁定A =>死锁)
在WatcherClass的情况下,如果你将要有多个watcherclass实例,每个实例都有自己的锁,请注意不要进行外部调用(或触发事件),这些调用最终可能会尝试获取其他watcherclass实例中的锁。这是一个等待发生的AB / BA死锁。