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

这让我很困惑,以至于我决定在这里问。
因此,假设是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");
        }
   });
}

最佳答案

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

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


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

07-24 15:32