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

问题描述 投票:2回答:2

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

这让我很困惑,我决定在这里问。因此,假设是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锁定到object中,如果相同的CanAppend返回false,则调用Flush方法,该方法也尝试锁定相同的对象。所以我希望在那里陷入僵局


了解了一点点后再推断,因为Lock是递归的,所以我们可以认为这也将起作用:

lock(locker){           
   Console.WriteLine("Hello World");
   await new Task(() => {
        lock(locker){                   
            Console.WriteLine("Hello World from locker");
        }
   });
}
c# multithreading locking deadlock
2个回答
6
投票

Monitor对象是C#,是递归的,因此您只需要记住将它们解锁的次数与锁定它们的次数相同。例如,这是完全有效的:

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

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


3
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.