试图调用异步方法同步。它等待的Task.Result永远 [英] Trying to call Async method synchronously. It waits on Task.Result forever

查看:142
本文介绍了试图调用异步方法同步。它等待的Task.Result永远的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以,我写的,我想揭露了一系列具有同步和异步等效方法的应用程序。要做到这一点,我想最简单的方法是写在asnyc方法的逻辑,写同步方法,围绕异步方法的包装,等待同步为他们提供他们的结果。在code是不是在玩球。在下面的code样品(不是我真正的code,但减少的基本问题),行 Console.WriteLine(结果)永远达不到 - 在preceding线挂到永远。奇怪的是,虽然,如果我或多或少逐字复制这种模式到一个控制台应用程序,它的工作原理。

我是什么做错了吗?这是一个简单的错误的思路,如果是这样,我应该用什么方式使用呢?

 公共部分类主窗口:窗口{

    公共主窗口(){
        this.InitializeComponent();
        VAR的结果=的MyMethod(); //从不返回
        Console.WriteLine(结果);
    }

    公共字符串的MyMethod(){
        返回MyMethodAsync()结果。 //挂起这里
    }

    公共异步任务<字符串> MyMethodAsync(){//想象这里的逻辑是更复杂
        使用(VAR CL =新的HttpClient()){
            返回等待cl.GetStringAsync(http://www.google.co.uk/);
        }
    }

}
 

解决方案

这是一个典型的僵局。用户界面正在等待异步方法来完成,但是异步方法改掉更新UI线程和 BOOM,僵局。

  

奇怪的是,虽然,如果我或多或少逐字复制这种模式成   控制台应用程序,它的工作原理。

这是因为你的WinForm应用程序有一个自定义的的SynchronizationContext 。它是隐含caputred和它的工作是回到工作元帅到UI线程一次是从你的等待

返回

如果你真的揭露各地的异步操作同步的包装? ,答案是

有一种出路,但我真的不喜欢它。如果你绝对的必须的(不)打电话给你的code同步(同样,你真的不应该),使用 ConfigureAwait(假)异步方法内。这将指示 awaitable 未捕获当前synccontext,所以它不会名帅下班回来到UI线程:

 公共异步任务<字符串> MyMethodAsync()
{
    使用(VAR CL =新的HttpClient())
    {
        返回等待cl.GetStringAsync(http://www.google.co.uk/)
                       .ConfigureAwait(假);
    }
}
 

请注意,如果你这样做,然后尝试以后调用任何UI元素,你将最终与 InvalidOperationException异常,因为你不会是在UI线程上

通过构造函数初始化用户界面是一个常见的​​模式。斯蒂芬·克利里有一个非常好的系列异步,你可以找到这里

  

我是什么做错了吗?这是一个简单的不良模式,如果是这样,什么   模式我应该用呢?

是的,绝对。如果你想揭露异步和同步的API,使用正确的API这不会让你进入这种情况(死锁)在firstcase。例如,如果要公开一个同步 DownloadString ,使用的 Web客户端 代替。

So I'm writing an application in which I want to expose a series of methods with both synchronous and asynchronous equivalents. To do this, I figured the easiest approach was to write the logic in the asnyc method, and write synchronous methods as wrappers around the async methods, waiting synchronously for them to deliver their results. The code isn't playing ball. In the following code sample (not my real code but a reduction of the basic problem), the line Console.WriteLine(result) is never reached - the preceding line hangs forever. Curiously though, if I copy this pattern more or less verbatim into a Console app, it works.

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

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/");
        }
    }

}

解决方案

This is a classic deadlock. The UI is waiting on the async method to finish, but the async method trys to update the UI thread and BOOM, deadlock.

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

That's because your WinForm application has a custom SynchronizationContext. It is caputred implicitly and its job is to marshal work back onto the UI thread once returning from your await.

Should you really expose synchronous wrappers around asynchronous operations?, the answer is no.

There is a way out of it, but i dont really like it. If you absolutely have to (you don't) call your code synchronously (again, you really shouldn't), use ConfigureAwait(false) inside the async method. This instructs the awaitable not to capture the current synccontext, so it wont marshal work back onto the UI thread:

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

Note that if you do this and then try to call any UI element afterwards, you'll end up with an InvalidOperationException since you won't be on the UI thread.

Initializing the UI via a constructor is a common pattern. Stephan Cleary has a very nice series on async which you can find here.

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

Yes, absolutely. If you want to expose both asynchronous and synchronous APIs, use the proper api's which wouldn't get you into this situation (a deadlock) in the firstcase. For example, if you want to expose a synchronous DownloadString, use WebClient instead.

这篇关于试图调用异步方法同步。它等待的Task.Result永远的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆