c# - 尝试同步调用 Async 方法。它永远等待 Task.Result

标签 c# .net asynchronous task-parallel-library async-await

<分区>

所以我正在编写一个应用程序,我想在其中公开一系列具有同步和异步等效项的方法。为此,我认为最简单的方法是在 asnyc 方法中编写逻辑,并将同步方法编写为异步方法的包装器,同步等待它们传递结果。该代码不是在玩球。在下面的代码示例中(不是我的真实代码,而是基本问题的缩减),永远不会到达 Console.WriteLine(result) 行 - 前面的行永远挂起。但奇怪的是,如果我或多或少地将此模式逐字复制到控制台应用程序中,它就会起作用。

我做错了什么?这仅仅是一个糟糕的模式吗?如果是这样,我应该改用什么模式?

public partial class MainWindow : Window {

    public MainWindow() {
        this.InitializeComponent();
        var result = MyMethod(); //Never returns
        Console.WriteLine(result);
    }

    public string MyMethod() {
        return MyMethodAsync().Result; //Hangs here
    }

    public async Task<string> MyMethodAsync() { //Imagine the logic here is more complex
        using (var cl = new HttpClient()) {
            return await cl.GetStringAsync("http://www.google.co.uk/");
        }
    }

}

最佳答案

这是一个典型的僵局。 UI 正在等待异步方法完成,但异步方法尝试更新 UI 线程和BOOM,死锁。

Curiously though, if I copy this pattern more or less verbatim into a Console app, it works.

那是因为您的 WinForm 应用程序有一个自定义的 SynchronizationContext。它是隐式捕获的,它的工作是在从 await 返回后将工作编码回 UI 线程。

Should you really expose synchronous wrappers around asynchronous operations? ,答案是

有一个解决办法,但我不太喜欢。如果您绝对必须(您不需要)同步调用您的代码(同样,您确实不应该),请在异步方法中使用ConfigureAwait(false)。这指示 awaitable 不要捕获当前同步上下文,因此它不会将工作编码回 UI 线程:

public async Task<string> MyMethodAsync() 
{ 
    using (var cl = new HttpClient()) 
    {
        return await cl.GetStringAsync("http://www.google.co.uk/")
                       .ConfigureAwait(false);
    }
}

请注意,如果您执行此操作然后尝试调用任何 UI 元素,您将以 InvalidOperationException 结束,因为您将不在 UI 线程上。

通过构造函数初始化 UI 是一种常见的模式。 Stephan Cleary 有一个非常好的异步系列,您可以找到它 here .

What am I doing wrong? Is this simply a bad pattern, and if so, what pattern should I be using instead?

是的,绝对是。如果您想公开异步和同步 API,请使用正确的 API,这不会让您在第一种情况下陷入这种情况(死锁)。例如,如果要公开同步 DownloadString,请使用 WebClient相反。

关于c# - 尝试同步调用 Async 方法。它永远等待 Task.Result,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26948818/

相关文章:

.net - c#.net 本地数据库 Windows 窗体

c# - 如何在使用 DbContext 保存对象后获取对象的主键

c# - 实体类型 'type' 处于阴影状态。有效的模型要求所有实体类型都具有相应的 CLR 类型

c# - 使用 Powershell 创建新的 EventSource 与 C# 不同吗?

c# - 如何添加视频广告?

javascript - 如何循环firebase的异步查询代码?

.net - 使用 Microsoft.Web.Administration 中的 ServerManager 类时出现 COM 异常

c# - Process.Start ("IExplore.exe"); <-- 这可靠吗?

c# - 如何在每个实现的异步中抛出取消异常?

swift - 解析嵌套的完成处理程序