Can't specify the 'async' modifier on

2018-12-31 03:08发布

I am new to asynchronous programming with the async modifier. I am trying to figure out how to make sure that my Main method of a console application actually runs asynchronously.

class Program
{
    static void Main(string[] args)
    {
        Bootstrapper bs = new Bootstrapper();
        var list = bs.GetList();
    }
}

public class Bootstrapper {

    public async Task<List<TvChannel>> GetList()
    {
        GetPrograms pro = new GetPrograms();

        return await pro.DownloadTvChannels();
    }
}

I know this is not running asynchronously from "the top." Since it is not possible to specify the async modifier on the Main method, how can I run code within main asynchronously?

15条回答
十年一品温如言
2楼-- · 2018-12-31 03:17

You can solve this with this simple construct:

class Program
{
    static void Main(string[] args)
    {
        Task.Run(async () =>
        {
            // Do any async anything you need here without worry
        }).GetAwaiter().GetResult();
    }
}

That will put everything you do out on the ThreadPool where you'd want it (so other Tasks you start/await don't attempt to rejoin a Thread they shouldn't), and wait until everything's done before closing the Console app. No need for special loops or outside libs.

Edit: Incorporate Andrew's solution for uncaught Exceptions.

查看更多
看风景的人
3楼-- · 2018-12-31 03:18

In C# 7.1 you will be able to do a proper async Main. The appropriate signatures for Main method has been extended to:

public static Task Main();
public static Task<int> Main();
public static Task Main(string[] args);
public static Task<int> Main(string[] args);

For e.g. you could be doing:

static async Task Main(string[] args)
{
    Bootstrapper bs = new Bootstrapper();
    var list = await bs.GetList();
}

At compile time, the async entry point method will be translated to call GetAwaitor().GetResult().

Details: https://blogs.msdn.microsoft.com/mazhou/2017/05/30/c-7-series-part-2-async-main

EDIT:

To enable C# 7.1 language features, you need to right-click on the project and click "Properties" then go to the "Build" tab. There, click the advanced button at the bottom:

enter image description here

From the language version drop-down menu, select "7.1" (or any higher value):

enter image description here

The default is "latest major version" which would evaluate (at the time of this writing) to C# 7.0, which does not support async main in console apps.

查看更多
余欢
4楼-- · 2018-12-31 03:18

In my case I had a list of jobs that I wanted to run in async from my main method, have been using this in production for quite sometime and works fine.

static void Main(string[] args)
{
    Task.Run(async () => { await Task.WhenAll(jobslist.Select(nl => RunMulti(nl))); }).GetAwaiter().GetResult();
}
private static async Task RunMulti(List<string> joblist)
{
    await ...
}
查看更多
余生请多指教
5楼-- · 2018-12-31 03:20

For asynchronously calling task from Main, use

  1. Task.Run() for .NET 4.5

  2. Task.Factory.StartNew() for .NET 4.0 (May require Microsoft.Bcl.Async library for async and await keywords)

Details: http://blogs.msdn.com/b/pfxteam/archive/2011/10/24/10229468.aspx

查看更多
谁念西风独自凉
6楼-- · 2018-12-31 03:24

Newest version of C# - C# 7.1 allows to create async console app. To enable C# 7.1 in project, you have to upgrade your VS to at least 15.3, and change C# version to C# 7.1 or C# latest minor version. To do this, go to Project properties -> Build -> Advanced -> Language version.

After this, following code will work:

internal class Program
{
    public static async Task Main(string[] args)
    {
         (...)
    }
查看更多
谁念西风独自凉
7楼-- · 2018-12-31 03:28

As you discovered, in VS11 the compiler will disallow an async Main method. This was allowed (but never recommended) in VS2010 with the Async CTP.

I have recent blog posts about async/await and asynchronous console programs in particular. Here's some background info from the intro post:

If "await" sees that the awaitable has not completed, then it acts asynchronously. It tells the awaitable to run the remainder of the method when it completes, and then returns from the async method. Await will also capture the current context when it passes the remainder of the method to the awaitable.

Later on, when the awaitable completes, it will execute the remainder of the async method (within the captured context).

Here's why this is a problem in Console programs with an async Main:

Remember from our intro post that an async method will return to its caller before it is complete. This works perfectly in UI applications (the method just returns to the UI event loop) and ASP.NET applications (the method returns off the thread but keeps the request alive). It doesn't work out so well for Console programs: Main returns to the OS - so your program exits.

One solution is to provide your own context - a "main loop" for your console program that is async-compatible.

If you have a machine with the Async CTP, you can use GeneralThreadAffineContext from My Documents\Microsoft Visual Studio Async CTP\Samples(C# Testing) Unit Testing\AsyncTestUtilities. Alternatively, you can use AsyncContext from my Nito.AsyncEx NuGet package.

Here's an example using AsyncContext; GeneralThreadAffineContext has almost identical usage:

using Nito.AsyncEx;
class Program
{
    static void Main(string[] args)
    {
        AsyncContext.Run(() => MainAsync(args));
    }

    static async void MainAsync(string[] args)
    {
        Bootstrapper bs = new Bootstrapper();
        var list = await bs.GetList();
    }
}

Alternatively, you can just block the main Console thread until your asynchronous work has completed:

class Program
{
    static void Main(string[] args)
    {
        MainAsync(args).GetAwaiter().GetResult();
    }

    static async Task MainAsync(string[] args)
    {
        Bootstrapper bs = new Bootstrapper();
        var list = await bs.GetList();
    }
}

Note the use of GetAwaiter().GetResult(); this avoids the AggregateException wrapping that happens if you use Wait() or Result.

Update, 2017-11-30: As of Visual Studio 2017 Update 3 (15.3), the language now supports an async Main - as long as it returns Task or Task<T>. So you can now do this:

class Program
{
    static async Task Main(string[] args)
    {
        Bootstrapper bs = new Bootstrapper();
        var list = await bs.GetList();
    }
}

The semantics appear to be the same as the GetAwaiter().GetResult() style of blocking the main thread. However, there's no language spec for C# 7.1 yet, so this is only an assumption.

查看更多
登录 后发表回答