在异步方法中使用锁
本文关键字:异步方法 | 更新日期: 2023-09-27 18:18:07
我有点怀疑下面的"library"方法是否会实际工作,或者最终会死锁。
我有一个需要保护的"旧"对象,而"lock"似乎是最好的工具,因为在整个"类"中有一些对该资源的可重入调用(SomeSharedResource)。
编辑:1)。对"Compute"的调用实际上会阻塞或更糟糕的死锁吗?
var finalResult = await Compute(3).ConfigureAwait(false); // will this block?
2)。如果是有人干的呢?
var finalResult = Compute(3).Result; // will this block/deadlock?
3.) 2个线程同时调用:
var finalResult = await Compute(3).ConfigureAwait(false); // will this block?
所讨论的方法:
private readonly object lockObject = new object();
private async Task<double> Compute(int input)
{
double result;
lock (lockObject) {
result = SomeSharedResource(input);
}
return await ComputeAsync(result).ConfigureAwait(false); // some other awaitable method
}
-
呼叫
Compute(int input)
当然可以阻塞。您以同步方式lock (lockobject)
。如果一个线程持有lockObject,而另一个线程试图获取它,则第二个线程将在等待第一个线程完成时阻塞。 -
这可能会阻塞,原因与#1相同。它不会因为我们在这里看到的而死锁,因为结果延续没有继承同步上下文。它还将阻塞同步等待
ComputeAsync()
的结果,因为它使用.Result
-
这可能会阻塞,原因与#1相同。
锁和延续是不同的概念。虽然我不能保证您的程序不会死锁,但这是因为我无法看到您的整个程序。这里所展示的内容不会导致死锁(当然,典型的警告是:.Result
除外)。
但是要认识到lock()的存在实际上是在宣传执行线程被阻塞的可能性。在一些同步执行的代码中,async
- await
关键字对锁没有任何神奇的作用。
这些问题都没有实际答案。这些问题的答案都是"是的,如果有足够的操作条件,它们可以死锁"。
如果你的意思是"lock
语句会导致阻塞或死锁吗?",那么答案是阻塞是肯定的,死锁是否定的(假设SomeSharedResource(input)
行为良好,即内部不会死锁)。
对于问题1、2和3,答案是,"它们可能不会死锁"。虽然多个同时调用lock
节将阻塞,因为它们不能同时执行,这并不意味着它们将死锁。
你需要检查lock
内部发生了什么来确定。
危险在于,如果lock
内部的函数产生阻塞异步调用(await
或同等),最终调用回Compute()
或其他同样锁定lockObject
的代码。
只要lock
内部的函数没有阻塞另一个线程,您应该是安全的。如果存在阻塞异步调用,则需要验证它们不会重新进入,或者确保不会阻塞它们。
下面是一些该做和不该做的例子:
private async Task<double> DoSomething()
{
return await Compute(0);
}
private readonly object lockObject = new object();
private async Task<double> Compute(int input)
{
double result;
lock (lockObject) {
//This would be bad.
result = await DoSomething();
//So would this. If you didn't wait inside the lock you wouldn't deadlock though.
var t = DoSomething();
result = t.Result;
//This would be okay because it doesn't call back to Compute()
using (var stream = File.OpenText("test.txt"))
{
var contents = await stream.ReadToEndAsync();
}
}
return await ComputeAsync(result).ConfigureAwait(false); // some other awaitable method
}