我是使用async修饰符进行异步编程的新手。我试图弄清楚如何确保Main控制台应用程序的方法实际上异步运行。
async
Main
class Program { static void Main(string[] args) { Bootstrapper bs = new Bootstrapper(); var list = bs.GetList(); } } public class Bootstrapper { public async Task<List<TvChannel>> GetList() { GetPrograms pro = new GetPrograms(); return await pro.DownloadTvChannels(); } }
我知道这不是从“顶部”异步运行的。由于无法async在Main方法上指定修饰符,因此如何在main异步方式内运行代码?
main
如您所见,在VS11中,编译器将禁止使用async Main方法。在VS2010中,使用Async CTP允许(但绝不建议这样做)。
async Main
我最近在博客文章中特别提到了异步/等待和异步控制台程序。以下是介绍性帖子中的一些背景信息:
如果“ await”看到awaitable尚未完成,则它将异步执行。它告诉等待的对象在完成时运行该方法的其余部分,然后从异步方法中 返回 。当等待方法将其余方法传递给等待方法时,它还将捕获当前 上下文 。 稍后,当awaitable完成时,它将执行async方法的其余部分(在捕获的上下文中)。
如果“ 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包中使用。
GeneralThreadAffineContext
AsyncContext
这是一个使用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。
GetAwaiter().GetResult()
AggregateException
Wait()
Result
更新,2017-11-30: 从Visual Studio 2017更新3(15.3)开始,该语言现在支持async Main-,只要它返回Task或即可Task<T>。因此,您现在可以执行以下操作:
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的语言规范,因此这只是一个假设。