虽然代码回顾了自己的工作,但发现了一段非常有趣的代码,可以向我保证它会导致死锁,但是我已经对它进行了很多次测试,使用了多个线程,但是没有得到任何结果。
这让我很困惑,我决定在这里问。因此,假设是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");
}
});
}
Monitor
对象是C#,是递归的,因此您只需要记住将它们解锁的次数与锁定它们的次数相同。例如,这是完全有效的:
lock(someObject)
{
lock(someObject)
{
lock(someObject)
{
Consolw.WriteLine("hello world")
}
}
}
重要的是,认识到锁只有在获得锁后才是递归的。如果线程A已获取锁,然后线程B尝试获取锁,则B将阻塞,直到线程A释放锁。
对于死锁,您需要2个访问器和2个资源。在这种情况下,只有一种资源,因此每个人都将耐心等待。