正如你所发现的,在VS11中编译器将不允许async Main方法。在VS2010的Async CTP中,这是允许的(但从不推荐)。
更新,2017-11-30:从Visual Studio 2017更新3(15.3)开始,该语言现在支持异步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还没有语言规范,所以这只是一个假设。
我最近有一篇关于异步/等待和异步控制台程序的博客文章。以下是介绍帖子中的一些背景信息:
如果“await”看到可等待对象还没有完成,那么它就会异步操作。它告诉可等待对象在方法完成时运行该方法的剩余部分,然后从异步方法返回。当Await将方法的剩余部分传递给可等待对象时,它还将捕获当前上下文。
稍后,当可等待对象完成时,它将执行异步方法的剩余部分(在捕获的上下文中)。
以下是为什么这在带有异步Main的控制台程序中是一个问题:
请记住,在我们的介绍文章中,异步方法将在完成之前返回给它的调用者。这在UI应用程序(方法只返回到UI事件循环)和ASP。NET应用程序(该方法返回线程,但保持请求活动)。这对于控制台程序来说就不太适用了:Main返回到操作系统——所以你的程序退出了。
一个解决方案是提供你自己的上下文——一个异步兼容的控制台程序的“主循环”。
如果你有一台带有Async CTP的机器,你可以使用My Documents\Microsoft Visual Studio Async CTP\Samples(c# Testing) Unit Testing\AsyncTestUtilities中的GeneralThreadAffineContext。或者,你可以从我的Nito使用AsyncContext。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();这避免了使用Wait()或Result时发生的AggregateException包装。