我有一个公共异步无效Foo()方法,我想从同步方法调用。到目前为止,我从MSDN文档中看到的都是通过异步方法调用异步方法,但我的整个程序并不是用异步方法构建的。

这可能吗?

下面是一个从异步方法调用这些方法的例子: 演练:使用Async和Await访问Web (c#和Visual Basic)

现在我正在研究从sync方法调用这些async方法。


当前回答

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

其他回答

在尝试了几个小时不同的方法后,或多或少取得了成功,这就是我最终得到的结果。它在获取结果时不会以死锁结束,它还会获取并抛出原始异常,而不是被包装的异常。

private ReturnType RunSync()
{
  var task = Task.Run(async () => await myMethodAsync(agency));
  if (task.IsFaulted && task.Exception != null)
  {
    throw task.Exception;
  }

  return task.Result;
}

受到其他一些答案的启发,我创建了以下简单的帮助方法:

public static TResult RunSync<TResult>(Func<Task<TResult>> method)
{
    var task = method();
    return task.GetAwaiter().GetResult();
}

public static void RunSync(Func<Task> method)
{
    var task = method();
    task.GetAwaiter().GetResult();
}

调用方法如下(取决于你是否返回值):

RunSync(() => Foo());
var result = RunSync(() => FooWithResult());

注意,原始问题public async void Foo()中的签名是不正确的。它应该是公共async Task Foo(),因为对于不返回值的异步方法,应该返回Task而不是void(是的,有一些罕见的例外)。

您可以从同步代码中调用任何异步方法,直到您需要等待它们,在这种情况下,它们也必须被标记为异步。

正如很多人在这里建议的那样,您可以在同步方法中对结果任务调用Wait()或Result,但最终会在该方法中进行阻塞调用,这在某种程度上违背了异步的目的。

如果你真的不能让你的方法异步,你不想锁定同步方法,那么你将不得不使用一个回调方法,把它作为参数传递给任务上的ContinueWith()方法。

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方法。最大的区别是逻辑流没有中断,这使得读写更容易。

如果你想运行它同步

MethodAsync().RunSynchronously()