如您所见,在VS11中,编译器将禁止使用async Main
方法。在VS2010中,使用Async CTP允许(但绝不建议这样做)。
我最近在博客文章中特别提到了异步/等待和异步控制台程序。以下是介绍性帖子的一些背景信息:
如果“ await”看到awaitable尚未完成,则它将异步执行。它告诉可等待的对象在完成时运行该方法的其余部分,然后从异步方法中返回。当等待方法将其余方法传递给等待方法时,它还将捕获当前上下文。
稍后,当awaitable完成时,它将执行async方法的其余部分(在捕获的上下文中)。
这就是在控制台程序中使用出现问题的原因async Main
:
请记住,在我们的介绍性帖子中,异步方法将在完成之前返回其调用方。这在UI应用程序(该方法仅返回到UI事件循环)和ASP.NET应用程序(该方法从线程返回但使请求保持活动状态)中完美地工作。对于Console程序,效果不是很好:Main返回OS-因此程序退出。
一种解决方案是提供您自己的上下文-异步兼容的控制台程序的“主循环”。
如果您有一台具有异步CTP的计算机,则可以GeneralThreadAffineContext
从“ 我的文档” \“ Microsoft Visual Studio”中,使用“异步CTP” \“样本(C#测试)”单元测试\“ AsyncTestUtilities”。或者,您可以AsyncContext
从我的Nito.AsyncEx NuGet包中使用。
这是一个使用AsyncContext
; 的例子。GeneralThreadAffineContext
具有几乎相同的用法:
using Nito.AsyncEx;
class Program
{
static void Main(string[] args)
{
AsyncContext.Run(() => MainAsync(args));
}
static async void MainAsync(string[] args)
{
Bootstrapper bs = new Bootstrapper();
var list = await bs.GetList();
}
}
或者,您可以仅阻塞主控制台线程,直到异步工作完成:
class Program
{
static void Main(string[] args)
{
MainAsync(args).GetAwaiter().GetResult();
}
static async Task MainAsync(string[] args)
{
Bootstrapper bs = new Bootstrapper();
var list = await bs.GetList();
}
}
注意使用GetAwaiter().GetResult()
; 这样可以避免AggregateException
使用Wait()
或时发生的换行Result
。
更新,2017-11-30:从Visual Studio 2017更新3(15.3)开始,该语言现在支持async Main
-,只要它返回Task
或即可Task<T>
。因此,您现在可以执行以下操作:
class Program
{
static async Task Main(string[] args)
{
Bootstrapper bs = new Bootstrapper();
var list = await bs.GetList();
}
}
语义似乎与GetAwaiter().GetResult()
阻塞主线程的样式相同。但是,尚无C#7.1的语言规范,因此这只是一个假设。