コンソール アプリの 'Main' メソッドに 'async' 修飾子を指定できません 質問する

コンソール アプリの 'Main' メソッドに 'async' 修飾子を指定できません 質問する

私は、修飾子を使用した非同期プログラミングを初めて使用します。コンソール アプリケーションのメソッドが実際に非同期で実行されるasyncようにするにはどうすればよいかを考えています。Main

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

これは「トップ」から非同期的に実行されていないことはわかっています。メソッドasyncに修飾子を指定できないためMain、内部でコードを非同期的に実行するにはどうすればよいですかmain?

ベストアンサー1

Visual Studio 2012 では、コンパイラはasync Mainメソッドを許可しません。これは、Visual Studio 2010 の Async CTP では許可されていました (ただし、推奨されませんでした)。

Visual Studio 2017 Update 3 (15.3) 以降、言語は、またはasync Mainを返す限り、- をサポートするようになりました。そのため、次のように実行できるようになりました。TaskTask<T>

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

セマンティクスはメイン スレッドをブロックするスタイルと同じように見えますGetAwaiter().GetResult()。ただし、C# 7.1 の言語仕様はまだないため、これは単なる推測です。


私はブログに次のような記事を投稿しています非同期/待機そして非同期コンソールプログラム特に。紹介記事からの背景情報は次のとおりです。

「await」は、待機可能オブジェクトが完了していないと判断すると、非同期的に動作します。待機可能オブジェクトに、メソッドの残りが完了したら実行するように指示し、その後、非同期メソッドから戻ります。また、await は、メソッドの残りを待機可能オブジェクトに渡すときに、現在のコンテキストをキャプチャします。

その後、待機可能が完了すると、非同期メソッドの残りが実行されます (キャプチャされたコンテキスト内)。

コンソール プログラムでこれが問題になる理由は次のとおりですasync Main

非同期メソッドは完了する前に呼び出し元に戻ることを、紹介記事で説明しました。これは、UI アプリケーション (メソッドは UI イベント ループに戻るだけ) と ASP.NET アプリケーション (メソッドはスレッドから戻りますが、リクエストは存続します) では完全に機能します。コンソール プログラムではうまく機能しません。Main は OS に戻るため、プログラムは終了します。

1 つの解決策は、独自のコンテキスト、つまり非同期互換のコンソール プログラムの「メイン ループ」を提供することです。

Async CTPがインストールされているマシンをお持ちの場合は、My Documents\Microsoft Visual Studio Async CTP\Samples(C# Testing) Unit Testing\AsyncTestUtilitiesGeneralThreadAffineContextから使用できます。または、AsyncContextから私の Nito.AsyncEx NuGet パッケージ

以下は、 ; を使用した例ですAsyncContextが、GeneralThreadAffineContext使用法はほぼ同じです。

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

あるいは、非同期作業が完了するまでメインのコンソール スレッドをブロックすることもできます。

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

;の使用に注意してくださいGetAwaiter().GetResult()。これにより、または をAggregateException使用した場合に発生する折り返しが回避されます。Wait()Result

おすすめ記事