我有一个公共异步无效Foo()方法,我想从同步方法调用。到目前为止,我从MSDN文档中看到的都是通过异步方法调用异步方法,但我的整个程序并不是用异步方法构建的。
这可能吗?
下面是一个从异步方法调用这些方法的例子: 演练:使用Async和Await访问Web (c#和Visual Basic)
现在我正在研究从sync方法调用这些async方法。
我有一个公共异步无效Foo()方法,我想从同步方法调用。到目前为止,我从MSDN文档中看到的都是通过异步方法调用异步方法,但我的整个程序并不是用异步方法构建的。
这可能吗?
下面是一个从异步方法调用这些方法的例子: 演练:使用Async和Await访问Web (c#和Visual Basic)
现在我正在研究从sync方法调用这些async方法。
当前回答
这是最简单的解决办法。我在网上的某个地方看到它,我不记得在哪里了,但我一直在成功地使用它。它不会死锁调用线程。
void SynchronousFunction()
{
Task.Run(Foo).Wait();
}
string SynchronousFunctionReturnsString()
{
return Task.Run(Foo).Result;
}
string SynchronousFunctionReturnsStringWithParam(int id)
{
return Task.Run(() => Foo(id)).Result;
}
其他回答
这是最简单的解决办法。我在网上的某个地方看到它,我不记得在哪里了,但我一直在成功地使用它。它不会死锁调用线程。
void SynchronousFunction()
{
Task.Run(Foo).Wait();
}
string SynchronousFunctionReturnsString()
{
return Task.Run(Foo).Result;
}
string SynchronousFunctionReturnsStringWithParam(int id)
{
return Task.Run(() => Foo(id)).Result;
}
async Main现在是c# 7.2的一部分,可以在项目的高级构建设置中启用。
对于c# < 7.2,正确的方法是:
static void Main(string[] args)
{
MainAsync().GetAwaiter().GetResult();
}
static async Task MainAsync()
{
/*await stuff here*/
}
你会在很多微软文档中看到这个用法,例如: https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-dotnet-how-to-use-topics-subscriptions
如果你想运行它同步
MethodAsync().RunSynchronously()
异步编程的确是通过代码库“增长”的。它被比作僵尸病毒。最好的解决办法是让它成长,但有时这是不可能的。
我在我的Nito中写了一些类型。用于处理部分异步代码库的AsyncEx库。然而,没有一种解决方案适用于所有情况。
解决方案一
如果您有一个简单的异步方法,不需要同步回上下文,那么您可以使用Task。WaitAndUnwrapException:
var task = MyAsyncMethod();
var result = task.WaitAndUnwrapException();
您不想使用Task。等待或任务。结果,因为它们在AggregateException中包装了异常。
该解决方案仅适用于MyAsyncMethod不同步回上下文的情况。换句话说,MyAsyncMethod中的每个await都应该以ConfigureAwait(false)结束。这意味着它不能更新任何UI元素或访问ASP。NET请求上下文。
解决方案B
如果MyAsyncMethod需要同步回它的上下文,那么你可以使用AsyncContext。RunTask提供一个嵌套的上下文:
var result = AsyncContext.RunTask(MyAsyncMethod).Result;
*更新4/14/2014:在库的最新版本中,API如下:
var result = AsyncContext.Run(MyAsyncMethod);
(可以使用Task。因为RunTask将传播Task异常)。
你可能需要AsyncContext的原因。RunTask而不是Task。WaitAndUnwrapException是由于在WinForms/WPF/SL/ASP上可能会发生相当微妙的死锁。NET:
A synchronous method calls an async method, obtaining a Task. The synchronous method does a blocking wait on the Task. The async method uses await without ConfigureAwait. The Task cannot complete in this situation because it only completes when the async method is finished; the async method cannot complete because it is attempting to schedule its continuation to the SynchronizationContext, and WinForms/WPF/SL/ASP.NET will not allow the continuation to run because the synchronous method is already running in that context.
这就是为什么在每个异步方法中尽可能使用ConfigureAwait(false)是一个好主意的原因之一。
解决方案C
AsyncContext。RunTask并不适用于所有场景。例如,如果异步方法等待需要UI事件才能完成的事情,那么即使嵌套上下文也会死锁。在这种情况下,你可以在线程池上启动async方法:
var task = Task.Run(async () => await MyAsyncMethod());
var result = task.WaitAndUnwrapException();
但是,这个解决方案需要在线程池上下文中工作的MyAsyncMethod。所以它不能更新UI元素或访问ASP。NET请求上下文。在这种情况下,您也可以将ConfigureAwait(false)添加到它的await语句中,并使用解决方案A。
更新日期:2019-05-01:当前的“最不坏的做法”见MSDN的一篇文章。
public async Task<string> StartMyTask()
{
await Foo()
// code to execute once foo is done
}
static void Main()
{
var myTask = StartMyTask(); // call your method which will return control once it hits await
// now you can continue executing code here
string result = myTask.Result; // wait for the task to complete to continue
// use result
}
您将‘await’关键字读为“启动这个长时间运行的任务,然后将控制权返回给调用方法”。长时间运行的任务完成后,它将执行后面的代码。await之后的代码类似于过去的CallBack方法。最大的区别是逻辑流没有中断,这使得读写更容易。