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:29

In Main try changing the call to GetList to:

Task.Run(() => bs.GetList());
查看更多
公子世无双
3楼-- · 2018-12-31 03:32

You can do this without needing external libraries also by doing the following:

class Program
{
    static void Main(string[] args)
    {
        Bootstrapper bs = new Bootstrapper();
        var getListTask = bs.GetList(); // returns the Task<List<TvChannel>>

        Task.WaitAll(getListTask); // block while the task completes

        var list = getListTask.Result;
    }
}
查看更多
时光乱了年华
4楼-- · 2018-12-31 03:32

When the C# 5 CTP was introduced, you certainly could mark Main with async... although it was generally not a good idea to do so. I believe this was changed by the release of VS 2013 to become an error.

Unless you've started any other foreground threads, your program will exit when Main completes, even if it's started some background work.

What are you really trying to do? Note that your GetList() method really doesn't need to be async at the moment - it's adding an extra layer for no real reason. It's logically equivalent to (but more complicated than):

public Task<List<TvChannel>> GetList()
{
    return new GetPrograms().DownloadTvChannels();
}
查看更多
旧时光的记忆
5楼-- · 2018-12-31 03:33

C# 7.1 (using vs 2017 update 3) introduces async main

You can write:

   static async Task Main(string[] args)
  {
    await ...
  }

For more details C# 7 Series, Part 2: Async Main

Update:

You may get a compilation error:

Program does not contain a static 'Main' method suitable for an entry point

This error is due to that vs2017.3 is configured by default as c#7.0 not c#7.1.

You should explicitly modify the setting of your project to set c#7.1 features.

You can set c#7.1 by two methods:

Method 1: Using the project settings window:

  • Open the settings of your project
  • Select the Build tab
  • Click the Advanced button
  • Select the version you want As shown in the following figure:

enter image description here

Method2: Modify PropertyGroup of .csproj manually

Add this property:

    <LangVersion>7.1</LangVersion>

example:

    <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
        <PlatformTarget>AnyCPU</PlatformTarget>
        <DebugSymbols>true</DebugSymbols>
        <DebugType>full</DebugType>
        <Optimize>false</Optimize>
        <OutputPath>bin\Debug\</OutputPath>
        <DefineConstants>DEBUG;TRACE</DefineConstants>
        <ErrorReport>prompt</ErrorReport>
        <WarningLevel>4</WarningLevel>
        <Prefer32Bit>false</Prefer32Bit>
        <LangVersion>7.1</LangVersion>
    </PropertyGroup>    
查看更多
人间绝色
6楼-- · 2018-12-31 03:34

If you're using C# 7.1 or later, go with the nawfal's answer and just change the return type of your Main method to Task or Task<int>. If you are not:

  • Have an async Task MainAsync like Johan said.
  • Call its .GetAwaiter().GetResult() to catch the underlying exception like do0g said.
  • Support cancellation like Cory said.
  • A second CTRL+C should terminate the process immediately. (Thanks binki!)
  • Handle OperationCancelledException - return an appropriate error code.

The final code looks like:

private static int Main(string[] args)
{
    var cts = new CancellationTokenSource();
    Console.CancelKeyPress += (s, e) =>
    {
        e.Cancel = !cts.IsCancellationRequested;
        cts.Cancel();
    };

    try
    {
        return MainAsync(args, cts.Token).GetAwaiter().GetResult();
    }
    catch (OperationCanceledException)
    {
        return 1223; // Cancelled.
    }
}

private static async Task<int> MainAsync(string[] args, CancellationToken cancellationToken)
{
    // Your code...

    return await Task.FromResult(0); // Success.
}
查看更多
与君花间醉酒
7楼-- · 2018-12-31 03:36

Haven't needed this much yet, but when I've used console application for Quick tests and required async I've just solved it like this:

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

    static async Task MainAsync(string[] args)
    {
        // Code here
    }
}
查看更多
登录 后发表回答