可以在CoreCLR上使用’async’修饰符标记入口点?
在Stephan Cleary最近关于.NET CoreCLR上的异步控制台应用程序的博客文章中,他向我们展示了在CoreCLR中(当前在Visual Studio 2015上运行,CTP6),入口点“Main”实际上可以标记为async Task
,正确编译并实际运行:
public class Program { public async Task Main(string[] args) { Console.WriteLine("Hello World"); await Task.Delay(TimeSpan.FromSeconds(1)); Console.WriteLine("Still here!"); Console.ReadLine(); } }
给出以下输出:
这是由ASP.NET团队的一篇名为“深度潜水”的博客文章强化到ASP.NET 5运行时 :
除了静态的
Program.Main
入口点,KRE还支持基于实例的入口点。 您甚至可以使主入口点异步并返回任务。 通过使主入口点成为实例方法,您可以将运行时环境中的服务注入到应用程序中。
我们知道,到目前为止, 无法使用’async’修饰符标记入口点 。 那么,在新的CoreCLR运行时中,这实际上是如何实现的呢?
深入了解CoreCLR运行时的源代码,我们可以看到一个名为RuntimeBootstrapper
的静态类,它负责调用我们的入口点:
public static int Execute(string[] args) { // If we're a console host then print exceptions to stderr var printExceptionsToStdError = Environment.GetEnvironmentVariable(EnvironmentNames.ConsoleHost) == "1"; try { return ExecuteAsync(args).GetAwaiter().GetResult(); } catch (Exception ex) { if (printExceptionsToStdError) { PrintErrors(ex); return 1; } throw; } }
我们可以在内部看到它调用ExecuteAsync(args).GetAwaiter().GetResult();
,在语义上等同于调用Task.Result
,除了不是接收包装的AggregationException
,我们收到解包的exception。
这一点很重要,因为没有关于它是如何发生的“黑魔法” 。 对于当前版本的CoreCLR运行时,允许该方法标记为async Task
因为它被运行时阻塞在调用链的上方。
附注:
潜入ExecuteAsync
,我们会看到它最终调用:
return bootstrapper.RunAsync(app.RemainingArguments);
在查看内部时 ,我们看到了入口点的实际MethodInfo
调用:
public static Task Execute(Assembly assembly, string[] args, IServiceProvider serviceProvider) { object instance; MethodInfo entryPoint; if (!TryGetEntryPoint(assembly, serviceProvider, out instance, out entryPoint)) { return Task.FromResult(-1); } object result = null; var parameters = entryPoint.GetParameters(); if (parameters.Length == 0) { result = entryPoint.Invoke(instance, null); } else if (parameters.Length == 1) { result = entryPoint.Invoke(instance, new object[] { args }); } if (result is int) { return Task.FromResult((int)result); } if (result is Task ) { return (Task )result; } if (result is Task) { return ((Task)result).ContinueWith(t => { return 0; }); } return Task.FromResult(0); }