我有一个公共异步无效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;
    }

其他回答

如果你想运行它同步

MethodAsync().RunSynchronously()

这些windows异步方法有一个漂亮的小方法叫做AsTask()。您可以使用它让方法作为任务返回本身,以便您可以手动调用Wait()。

例如,在Windows Phone 8 Silverlight应用程序中,您可以执行以下操作:

private void DeleteSynchronous(string path)
{
    StorageFolder localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
    Task t = localFolder.DeleteAsync(StorageDeleteOption.PermanentDelete).AsTask();
    t.Wait();
}

private void FunctionThatNeedsToBeSynchronous()
{
    // Do some work here
    // ....

    // Delete something in storage synchronously
    DeleteSynchronous("pathGoesHere");

    // Do other work here 
    // .....
}

希望这能有所帮助!

对于任何还在关注这个问题的人…

在微软。visualstudio。services。webapi中有一个叫TaskExtensions的类。在这个类中,你会看到静态扩展方法task . syncresult(),它会完全阻塞线程,直到任务返回。

在内部,它调用Task . getawaiter ().GetResult(),这是非常简单的,但是它重载工作在任何异步方法,返回Task, Task<T>或Task<HttpResponseMessage>…语法糖,宝贝…爸爸爱吃甜食。

getresult()是在阻塞上下文中执行异步代码的ms官方方式。似乎在我的用例中工作得很好。

斯蒂芬·克利里的回答;

这种方法应该不会导致死锁(假设 ProblemMethodAsync不发送更新到UI线程或任何东西 像这样)。它假设可以在对象上调用ProblemMethodAsync 线程池线程,这并不总是这样。

https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html

这就是方法;

线程池攻击与阻塞攻击类似的方法是 将异步工作卸载到线程池,然后阻塞 产生的任务。使用此黑客的代码看起来像下面的代码 如图7所示。 图7线程池攻击的代码 c#

public sealed class WebDataService : IDataService
{
  public string Get(int id)
  {
    return Task.Run(() => GetAsync(id)).GetAwaiter().GetResult();
  }
  public async Task<string> GetAsync(int id)
  {
    using (var client = new WebClient())
      return await client.DownloadStringTaskAsync(
      "https://www.example.com/api/values/" + id);
  }
}

Task的调用。Run在线程池上执行异步方法 线程。在这里,它将在没有上下文的情况下运行,从而避免 死锁。这种方法的一个问题是异步性 方法不能依赖于在特定上下文中执行。所以,它 不能使用UI元素或ASP。净HttpContext.Current。

异步编程的确是通过代码库“增长”的。它被比作僵尸病毒。最好的解决办法是让它成长,但有时这是不可能的。

我在我的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的一篇文章。