Why is async/await not working in my ASP.net 5 Con

2019-02-24 04:16发布

问题:

I tried this simple ASP.net 5 Console Application on both Windows (.NET 4.5.1) and Linux (Mono 4.0.1), both times with the same result.

Note: I called it an ASP.net 5 Console Application because that is what is was called in Visual Studio up to RC. Now it is called Console Application (Package), but it still uses DNX from https://github.com/aspnet/dnx :)

My Program.cs:

using System;
using System.Threading.Tasks;

namespace ConsoleApplication
{
    public class Program
    {
        public async void Main(String[] args)
        {
            #if DNX451
            AppDomain.CurrentDomain.UnhandledException += 
                (s, e) => Console.WriteLine(e);
            #endif

            try
            {
                await Task.Delay(1000);
                Console.WriteLine("After Task.Delay");
            }
            finally
            {
                Console.WriteLine("Inside Finally");
            }
        }
    }
}

My project.json:

{
    "version": "1.0.0-*",
    "dependencies": {},
    "commands": {
        "ConsoleApplication": "ConsoleApplication"
    },
    "frameworks": {
        "dnx451": {}
    }
}

When running with either 1.0.0-beta4 CLR or 1.0.0-beta5-11904 CLR, the command dnx . ConsoleApplication prints nothing. The program exits with status code 0 as soon as it encounters the Task.Delay. Even the finally block is never executed.

I couldn't test .NET Core 5.0 because dnu restore says everything is OK, but the packages can't be located by the runtime. Oh well...

Does anybody have the same problems with async/await and DNX? Or spot some error I made?

回答1:

If you see my question (and answer) in Entry point can be marked with the 'async' modifier on CoreCLR?, you'll see that at the top-most of the call-stack, you have the following:

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;
    }
}

Internally, it checks to see the return type of the method, if the return type is of type Task, then it registers a ContinueWith, which it'll be able to synchronously wait on:

if (result is Task)
{
    return ((Task)result).ContinueWith(t =>
    {
        return 0;
    });
}

When you pass in async void, it looks to Execute as if this method is a "fire and forget" void returning method. That's why it never finishes execution. But, if you change it to return a Task, it'll work:

public async Task Main(String[] args)
{
    #if DNX451
    AppDomain.CurrentDomain.UnhandledException += 
        (s, e) => Console.WriteLine(e);
    #endif

    try
    {
        await Task.Delay(1000);
        Console.WriteLine("After Task.Delay");
    }
    finally
    {
        Console.WriteLine("Inside Finally");
    }
}