【问题标题】:Entry point can be marked with the 'async' modifier on CoreCLR?入口点可以用 CoreCLR 上的“异步”修饰符标记吗?
【发布时间】:2015-05-10 09:46:16
【问题描述】:

在 Stephan Cleary 最近关于Async Console Apps on .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 团队的一篇名为 A Deep Dive into the ASP.NET 5 Runtime 的博客文章强化了这一点:

除了静态的Program.Main 入口点,KRE 还支持 基于实例的入口点。您甚至可以制作主要入口点 异步并返回一个任务。通过让主入口点成为 实例方法,您可以将服务注入到您的应用程序中 运行时环境。

到目前为止,我们都知道这一点,An entry point cannot be marked with the 'async' modifier。那么,在新的 CoreCLR 运行时中,这实际上是如何实现的呢?

【问题讨论】:

    标签: c# .net async-await coreclr


    【解决方案1】:

    深入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,而是未包装的异常。

    理解这一点很重要,因为对于它是如何发生的,没有“黑魔法”。对于当前版本的 CoreCLR 运行时,允许将该方法标记为 async Task,因为它被运行时阻塞在调用链的更高位置。

    旁注:

    深入ExecuteAsync,我们会看到它最终会调用:

    return bootstrapper.RunAsync(app.RemainingArguments);
    

    When looking inside,我们看到实际的 MethodInfo 调用我们的入口点:

    public static Task<int> 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<int>)
        {
            return (Task<int>)result;
        }
    
        if (result is Task)
        {
            return ((Task)result).ContinueWith(t =>
            {
                return 0;
            });
        }
    
        return Task.FromResult(0);
    }
    

    【讨论】:

      猜你喜欢
      • 2021-09-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-12
      • 2019-06-01
      相关资源
      最近更新 更多