私は、修飾子を使用した非同期プログラミングを初めて使用します。コンソール アプリケーションのメソッドが実際に非同期で実行される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
を返す限り、- をサポートするようになりました。そのため、次のように実行できるようになりました。Task
Task<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