从非异步代码调用异步方法 [英] Calling async methods from non-async code

查看:93
本文介绍了从非异步代码调用异步方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在更新具有.NET 3.5中内置的API表面的库.结果,所有方法都是同步的.我无法更改API(即,将返回值转换为Task),因为这将要求所有调用者都进行更改.因此,我剩下了如何最好地以同步方式调用异步方法.这是在ASP.NET 4,ASP.NET Core和.NET/.NET Core控制台应用程序的上下文中进行的.

I'm in the process of updating a library that has an API surface that was built in .NET 3.5. As a result, all methods are synchronous. I can't change the API (i.e., convert return values to Task) because that would require that all callers change. So I'm left with how to best call async methods in a synchronous way. This is in the context of ASP.NET 4, ASP.NET Core, and .NET/.NET Core console applications.

我可能还不够清楚-情况是我有不支持异步的现有代码,并且我想使用仅支持异步方法的新库,例如System.Net.Http和AWS开发工具包.因此,我需要弥合差距,并能够拥有可以被同步调用但可以在其他地方调用异步方法的代码.

I may not have been clear enough - the situation is that I have existing code that is not async aware, and I want to use new libraries such as System.Net.Http and the AWS SDK that support only async methods. So I need to bridge the gap, and be able to have code that can be called synchronously but then can call async methods elsewhere.

我已经读了很多书,而且有很多次被问及回答了.

I've done a lot of reading, and there are a number of times this has been asked and answered.

从非异步方法中调用异步方法

同步等待一个异步操作,为什么Wait()在这里冻结程序

从同步方法中调用异步方法

我将如何运行异步Task< T>同步方法?

同步调用异步方法

如何从同步方法中调用异步方法C#?

问题在于大多数答案都不一样!我见过的最常见的方法是使用.Result,但这会导致死锁.我已经尝试了以下所有方法,并且它们都能正常工作,但是我不确定哪种方法可以避免死锁,具有良好的性能并且可以在运行时很好地发挥作用(在尊重任务调度程序,任务创建选项等方面) ).有明确的答案吗?最好的方法是什么?

The problem is that most of the answers are different! The most common approach I've seen is use .Result, but this can deadlock. I've tried all the following, and they work, but I'm not sure which is the best approach to avoid deadlocks, have good performance, and plays nicely with the runtime (in terms of honoring task schedulers, task creation options, etc). Is there a definitive answer? What is the best approach?

private static T taskSyncRunner<T>(Func<Task<T>> task)
    {
        T result;
        // approach 1
        result = Task.Run(async () => await task()).ConfigureAwait(false).GetAwaiter().GetResult();

        // approach 2
        result = Task.Run(task).ConfigureAwait(false).GetAwaiter().GetResult();

        // approach 3
        result = task().ConfigureAwait(false).GetAwaiter().GetResult();

        // approach 4
        result = Task.Run(task).Result;

        // approach 5
        result = Task.Run(task).GetAwaiter().GetResult();


        // approach 6
        var t = task();
        t.RunSynchronously();
        result = t.Result;

        // approach 7
        var t1 = task();
        Task.WaitAll(t1);
        result = t1.Result;

        // approach 8?

        return result;
    }

推荐答案

所以我剩下如何以同步方式最好地调用异步方法了.

So I'm left with how to best call async methods in a synchronous way.

首先,这是一件可以做的事情.我之所以这样说,是因为在Stack Overflow上经常将其指出为恶魔的行为,作为笼统的声明而不考虑具体情况.

First, this is an OK thing to do. I'm stating this because it is common on Stack Overflow to point this out as a deed of the devil as a blanket statement without regard for the concrete case.

为了正确性,并不一定要一直保持同步.阻止异步使其同步具有可能会影响性能或完全不相关的性能成本.这要视具体情况而定.

It is not required to be async all the way for correctness. Blocking on something async to make it sync has a performance cost that might matter or might be totally irrelevant. It depends on the concrete case.

死锁来自试图同时进入同一单线程同步上下文的两个线程.避免这种情况的任何技术都可以可靠地避免由于阻塞而导致的死锁.

Deadlocks come from two threads trying to enter the same single-threaded synchronization context at the same time. Any technique that avoids this reliably avoids deadlocks caused by blocking.

在这里,您对.ConfigureAwait(false)的所有呼叫都没有意义,因为您没有等待.

Here, all your calls to .ConfigureAwait(false) are pointless because you are not awaiting.

RunSynchronously不能使用,因为不是所有任务都可以那样处理.

RunSynchronously is invalid to use because not all tasks can be processed that way.

.GetAwaiter().GetResult()Result/Wait()的不同之处在于,它模仿了await异常传播行为.您需要确定是否想要. (因此,请研究该行为是什么;无需在这里重复.)

.GetAwaiter().GetResult() is different from Result/Wait() in that it mimics the await exception propagation behavior. You need to decide if you want that or not. (So research what that behavior is; no need to repeat it here.)

此外,所有这些方法都具有相似的性能.他们将以一种或另一种方式分配OS事件并对其进行阻止.那是昂贵的部分.我不知道哪种方法绝对最便宜.

Besides that, all these approaches have similar performance. They will allocate an OS event one way or another and block on it. That's the expensive part. I don't know which approach is absolutely cheapest.

我个人喜欢Task.Run(() => DoSomethingAsync()).Wait();模式,因为它可以避免死锁,它很简单,并且不会隐藏GetResult()可能隐藏的某些异常.但是您也可以在此使用GetResult().

I personally like the Task.Run(() => DoSomethingAsync()).Wait(); pattern because it avoids deadlocks categorically, is simple and does not hide some exceptions that GetResult() might hide. But you can use GetResult() as well with this.

这篇关于从非异步代码调用异步方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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