Точка входа может быть отмечена модификатором «асинхронный» в CoreCLR?

В недавнем сообщении в блоге Стефана Клири о асинхронных консольных приложениях. на .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 среда выполнения ASP.NET 5:

В дополнение к статической точке входа Program.Main KRE поддерживает точки входа на основе экземпляров. Вы даже можете сделать основную точку входа асинхронной и вернуть Task. Имея основной точкой входа метод экземпляра, вы можете внедрить сервисы в ваше приложение средой выполнения.

Мы знаем, что до сих пор точка входа не может быть помечены модификатором 'async'. Итак, как это возможно в новой среде выполнения CoreCLR?


person Yuval Itzchakov    schedule 09.03.2015    source источник


Ответы (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);

При взгляде внутрь , мы видим, что фактический 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);
}
person Yuval Itzchakov    schedule 09.03.2015