簡體   English   中英

在ASP.NET的上下文中,為什么在調用異步方法時Task.Run(...).Result沒有死鎖?

[英]In the context of ASP.NET, why doesn't Task.Run(…).Result deadlock when calling an async method?

我用一個控制器和一個方法創建了一個簡單的WebApi項目:

public static class DoIt
{
    public static async Task<string> GetStrAsync(Uri uri)
    {
        using (var client = new HttpClient())
        {
            var str = await client.GetStringAsync(uri);
            return str;
        }
    }
}

public class TaskRunResultController : ApiController
{
    public string Get()
    {
        var task = Task.Run(() =>
            DoIt.GetStrAsync(new Uri("http://google.com"))
        );
        var result = task.Result;

        return result;
    }
}

我非常了解async / await和tasks; 斯蒂芬克萊里幾乎虔誠地跟隨他。 只是存在.Result讓我焦慮,我希望這會陷入僵局。 我知道Task.Run(...)是浪費的,導致在等待異步DoIt()完成時占用一個線程。

問題是這不是死鎖 ,而是讓我心悸。

我看到一些答案,如https://stackoverflow.com/a/32607091/1801382 ,我還發現當lambda正在執行時, SynchronizationContext.Current為null。 但是,我也有類似的問題,問為什么上面的代碼會出現死鎖,而且我看到在使用了ConfigureAwait(false) (不捕獲上下文)和.Result情況下發生了死鎖。

是什么賦予了?

Result本身不會導致死鎖。 死鎖是指代碼的兩個部分都在等待彼此。 Result只是一次等待,因此它可能是死鎖的一部分,但它不一定總是導致死鎖。

標准示例中Result 在保持上下文的同時等待任務完成,但是任務無法完成,因為它正在等待上下文空閑 所以有一個僵局 - 他們正在等待對方。

ASP.NET Core根本沒有上下文 ,因此Result不會死鎖。 (由於其他原因,它仍然不是一個好主意,但它不會陷入僵局 )。

問題是這不是死鎖,而是讓我心悸。

這是因為Task.Run任務不需要完成上下文。 所以調用Task.Run(...).Result是安全的。 Result正在等待任務完成,並且它保持上下文(阻止請求的任何其他部分在該上下文中執行),但這沒關系,因為Task.Run任務根本不需要上下文。

在ASP.NET上, Task.Run仍然不是一個好主意(出於其他原因),但這是一種非常有用的技術,不時有用。 它不會死鎖,因為Task.Run任務不需要上下文。

但是,我也有類似的問題,問為什么上面的代碼會出現死鎖,

相似但不完全相同。 仔細查看這些問題中的每個代碼語句,並問自己它運行的上下文。

我已經看到在與.Result一起使用ConfigureAwait(false)(不捕獲上下文)的情況下會發生死鎖。

Result /上下文死鎖是一個非常常見的 - 可能是最常見的死鎖。 但它不是那里唯一的僵局。

這是一個更加困難的死鎖示例 ,如果您 async代碼中阻塞( 沒有上下文),則會出現這種死鎖。 但是,這種情況更為罕見。

我會以相反的方式給你一個簡短的答案:如何產生死鎖:

讓我們從一個執行同步的Click處理程序開始,並將一些異步調用卸載到一個單獨的Task,然后等待結果

private void MenuItem_Click(object sender, RoutedEventArgs e)
{
    var t = Task.Run(() => DeadlockProducer(sender as MenuItem));
    var result = t.Result;
}

private async Task<int> DeadlockProducer(MenuItem sender)
{
    await Task.Delay(1);
    Dispatcher.Invoke(() => sender.Header = "Life sucks");
    return 0;
}

異步方法正在做另一件壞事:它試圖同步帶來一些UI更改,但UI線程仍在等待任務結果...

所以基本上, Task.Run已經完成了一半,並且可以用於很多格式良好的異步代碼,但是有很多方法可以解決它,所以它不是一個可靠的解決方案。

這個示例代碼是在WPF的上下文中編寫的,但我想ASP.Net可能會被濫用來產生類似的行為。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM