在异步方法中使用lock

时间:2015-10-16 05:35:49

标签: c# .net async-await

我怀疑"图书馆"下面的方法实际上会起作用,或者最终会死锁。

我有一个" old"需要保护的对象," 锁定 "似乎是最好的工具,因为在整个"类"中有一些对此资源的重新调用( 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

}

3 个答案:

答案 0 :(得分:2)

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

  2. 出于与#1相同的原因,这可能会阻止。它不会基于我们在此处看到的内容死锁,因为结果延续不会继承同步上下文。它还会阻止同步等待ComputeAsync()的结果,因为它使用了.Result

  3. 出于与#1相同的原因,这可能会阻止。

  4. 锁定和延续是不同的概念。虽然我不能保证你的程序不会陷入僵局,但这是因为我无法看到你的整个程序。你在这里展示的任何东西都没有乞求陷入僵局(当然,除了.Result之外的典型警告。)

    但是要认识到lock()的存在实际上是在宣告阻塞执行线程的可能性。 async - await个关键字在某些同步执行的代码中锁定不会产生任何魔力。

答案 1 :(得分:2)

这些问题都没有实际答案。他们的答案都是“是的,如果有足够的操作条件,它们就会陷入僵局”。

如果你的意思是“lock语句会导致阻塞还是死锁?”,那么阻塞的答案是肯定的,而死锁的答案是肯定的(假设SomeSharedResource(input)表现良好,即不内部僵局。

答案 2 :(得分:1)

对于问题1,2和3,答案是,&#34;他们可能不会遇到死锁&#34; 。但是,对lock部分的多个同时调用将会阻塞,因为它们无法同时执行,这并不意味着它们会死锁

您需要检查lock内发生的事情以确定。

如果lock内的函数产生阻塞异步调用(await或等效函数),最终会回调Compute()或其他同时锁定lockObject的代码,则会产生危险。 1}}。

只要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

}