c# - 使用相同的锁修改锁内的对象

标签 c# multithreading locking

我有一个由工作项集合组成的工作对象。每个作业都有自己的 WatcherClass 与之关联,它经常检查数据库以查看是否需要取消执行。它可以在工作流中的任何迭代中取消。如果它被取消,从 foreach block 运行的任何线程都将传播取消并正常退出。

我的观察程序代码中是否存在任何可能造成死锁的问题?我试图通过使用 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;
        }
    }
}

最佳答案

您在 Task 属性和 DoCheck 函数中获取的锁仅保护对 WatcherClass 的内部 _task 字段的访问。在 DoCheck 中,您还修改了 _task 对象本身的属性。该锁不会阻止任何其他人同时从其他线程修改任务对象的字段。

如果在您的应用程序中任务对象仅由 DoCheck 操作,那么您可能没问题。如果任务对象可能由 DoCheck 以外的代码操作,那么您可能会遇到问题。

另请记住,您创建的每一个额外的锁都是死锁的额外机会。如果始终以特定顺序获取多个锁,则它们可以是无死锁的。如果代码流允许在某些情况下在锁 B 之前获取锁 A,或者在其他情况下在锁 A 之前获取锁 B,那么您将面临严重的死锁风险。 (线程 1 锁定 A,尝试锁定 B,而线程 2 锁定 B 并尝试锁定 A => 死锁)

在您的 WatcherClass 案例中,如果您打算拥有多个 watcherclass 实例,每个实例都有自己的锁,请注意不要进行可能最终尝试获取其他 watcherclass 实例中的锁的外部调用(或触发事件)。这是等待发生的 AB/BA 僵局。

关于c# - 使用相同的锁修改锁内的对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4694911/

相关文章:

python - 为什么 multiprocessing.Queue 没有 task_done 方法

java - 什么可能导致程序在它似乎拥有的监视器上被阻止?

c# - 实现线程安全的队列或列表时,返回Count前是否需要加锁?

c# - 如何更新多个部分 View (一个主网格和另一个总计网格)?

c# - 如何将具体类注册到通用接口(interface)?

c++ - 使用 libcurl 多接口(interface)连续请求相同的 "easy"句柄

multithreading - 在Perl中,如何等待线程并行结束?

c# - 为什么在我的 Web API (ASP.NET v5) 项目中添加依赖项无法完全正常工作?

c# - 检查实体是否在 Code First 中的其他实体中有引用

javascript - Delphi - 无法在多线程应用程序中使用 IWebBrowser2 抑制 JavaScript 错误对话框