在异步方法中使用锁

本文关键字:异步方法 | 更新日期: 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
}

在异步方法中使用锁

  1. 呼叫Compute(int input)当然可以阻塞。您以同步方式lock (lockobject)。如果一个线程持有lockObject,而另一个线程试图获取它,则第二个线程将在等待第一个线程完成时阻塞。

  2. 这可能会阻塞,原因与#1相同。它不会因为我们在这里看到的而死锁,因为结果延续没有继承同步上下文。它还将阻塞同步等待ComputeAsync()的结果,因为它使用.Result

  3. 这可能会阻塞,原因与#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
}