CoreCLR上的入口点可以使用“async”修饰符标记吗?

6

在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();
    }
}

给出以下输出:

async Main entry point

这得益于 ASP.NET 团队的一篇博客文章A Deep Dive into the ASP.NET 5 Runtime

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

我们知道直到现在,入口点不能标记为 'async' 修饰符。那么,在新的 CoreCLR 运行时中,这是如何实现的呢?
1个回答

4
深入了解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);

当我们查看内部时, 我们会发现实际调用入口点的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);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接