簡體   English   中英

異步方法上的Task.Wait()在本地工作,但不能通過其他方法工作

[英]Task.Wait() on async method works locally, but not via another method

我最近解決了嘗試執行異步方法時遇到的問題。 對我來說,這提出了比解決還多的問題。 簡而言之,我很高興它能起作用,但我不知道為什么會起作用。

我試圖運行Microsoft Graph Client庫的以下方法:

_graphServiceClient.Users[userPrincipalName].Request().GetAsync()

以下方法不起作用,因為它在使用await后掛起:

async Task<User> GetUser(string userPrincipalName)
{
    User user = await _graphServiceClient.Users[userPrincipalName].Request().GetAsync();
    return user;
}

以下方法也不起作用,因為它在執行runTask.Wait()之后掛起:

User GetUser(string userPrincipalName)
{   
    return (User) GetResult(_graphServiceClient.Users[userPrincipalName].Request().GetAsync());
}

object GetResult<TResult>(Task<TResult> task)
{
    using (task)
    using (var runTask = Task.Run(async () => await task))
    {
        try
        {
            runTask.Wait();
            return runTask.Result;
        }
        catch (AggregateException e)
        {
            throw e.InnerException ?? e;
        }
    }
}

這是奇怪的地方,因為以下代碼確實起作用:

User GetUser(string userPrincipalName)
{   
    using (var task = Task.Run(async () => await _graphServiceClient.Users[userPrincipalName].Request().GetAsync()))
    {
        try
        {
            task.Wait();
            return task.Result;
        }
        catch (AggregateException e)
        {
            throw e.InnerException ?? e;
        }
    }
}

雖然我意識到后兩種方法不是最佳實踐,但我對第三種方法為何有效而第二種方法無效卻完全感到困惑。 在我看來,這些幾乎相同。

那么為什么第三種方法行得通,而第二種方法行不通?

首先,您需要了解僵局是如何發生的 await (默認情況下)將捕獲“上下文”,並將在該上下文中繼續執行其余的async方法 在ASP.NET Classic情況下,此“上下文”是請求上下文, 它一次僅允許一個線程運行 在線程池線程上運行時,“上下文”是線程池上下文,它將async方法的其余部分排隊到線程池中,在此線程中任何線程池線程都可以運行它。

阻止異步代碼是一種反模式 在具有單線程上下文的情況下(例如ASP.NET Classic),您可能會陷入死鎖。 在第一個死鎖示例中,在ASP.NET Classic上下文中調用了GetUser ,因此它的await將捕獲該上下文。 然后,調用代碼(也在ASP.NET Classic上下文中運行)將阻止該任務。 這會在該上下文中阻塞線程,從而阻止GetUser完成,因此最終會導致死鎖。

但是,即使您沒有陷入僵局,也仍然最終會首先放棄異步代碼的所有好處。 考慮“工作”示例,該示例屬於另一個反模式(ASP.NET上的Task.Run 在這種情況下, Task.Run導致GetAsync在線程池上下文中運行,因此在ASP.NET Classic上下文中阻止線程不會死鎖。 但是,這也是一種反模式。

正確的解決方案是始終保持async狀態 具體來說,您的控制器動作應該是async並在調用異步代碼時使用await 只需假裝Wait()Result不存在,您的代碼就會更加快樂。

暫無
暫無

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

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