为什么我们在这里没有死锁?

时间:2020-03-03 15:16:25

标签: c# multithreading locking deadlock

在回顾自己的工作时,我发现一段非常有趣的代码可以发誓会导致死锁,但是我已经对它进行了很多次测试,使用了多个线程,并且无法获得任何结果。

这让我很困惑,我决定在这里问。
因此,假设是LOCK不会为同一线程锁定,但是我想确认一下。
遵循这段代码

public class SplitService : ISplitService
{
    private IRecordService recordService;

    public SplitService(IRecordService recordService)
    {
        this.recordService = recordService;
    }

    private ConcurrentQueue<Batch> _batches = new ConcurrentQueue<Batch>();

    public void Feed(Something r)
    {
        lock (this.recordService)
        {
            if (!this.recordService.CanAppend(r))
            {
                Flush();
            }
            this.recordService.Append(r);
        }           
    }

    public void Flush()
    {
        lock (this.recordService)
        {
            if (!this.recordService.Any()) return;

            var record = this.recordService.GetBatch();
            _batches.Enqueue(record);

            this.recordService.Clean();
        }           
    }

    public IEnumerable<Batch> Get()
    {
        while (_batches.Any())
        {
            if (_batches.TryDequeue(out Batch batch))
            {
                yield return batch;
            }
        }
    }
}

您会注意到,方法Feed锁定在对象中,如果相同的CanAppend返回false,则调用Flush方法,该方法也锁定同一对象。 所以我希望在那里陷入僵局


在了解了一点点之后对其进行推断,由于Lock是递归的,因此我们可以假设这也可以:

lock(locker){           
   Console.WriteLine("Hello World");
   await new Task(() => {
        lock(locker){                   
            Console.WriteLine("Hello World from locker");
        }
   });
}

2 个答案:

答案 0 :(得分:6)

Monitor对象是C#递归对象,因此您只需要记住将它们解锁与锁定对象一样多即可。例如,这是完全正确的:

lock(someObject)
{
  lock(someObject)
  {
    lock(someObject)
    {
       Consolw.WriteLine("hello world")
    }
  }
}

重要的是要意识到,锁定只有在获得锁定后才是递归的。如果线程A已获取锁,然后线程B尝试获取锁,则B将阻塞,直到线程A释放锁为止。

答案 1 :(得分:3)

对于死锁,您需要2个访问器和2个资源。在这种情况下,只有一种资源,所以每个人都会耐心等待。