在回顾自己的工作时,我发现一段非常有趣的代码可以发誓会导致死锁,但是我已经对它进行了很多次测试,有多个线程,却没有得到任何结果。
这让我很困惑,以至于我决定在这里问。
因此,假设是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释放锁为止。