非同期プログラミングAsynchronous programming

I/O バインドのニーズ (ネットワークからのデータの要求、データベースへのアクセス、ファイル システムの読み書きなど) がある場合、非同期プログラミングを利用できます。If you have any I/O-bound needs (such as requesting data from a network, accessing a database, or reading and writing to a file system), you'll want to utilize asynchronous programming. CPU バインドのコードにも、コストのかかる計算の実行など、非同期コードに適したシナリオがあります。You could also have CPU-bound code, such as performing an expensive calculation, which is also a good scenario for writing async code.

C# は言語レベルで非同期プログラミング モデルを備えており、コールバックに苦労したり、非同期処理をサポートするライブラリに従ったりしなくても、非同期コードを簡単に記述できます。C# has a language-level asynchronous programming model, which allows for easily writing asynchronous code, without having to juggle callbacks or conform to a library that supports asynchrony. C# は、タスク ベースの非同期パターン (TAP) と呼ばれるものに従います。It follows what is known as the Task-based Asynchronous Pattern (TAP).

非同期モデルの概要Overview of the asynchronous model

非同期プログラミングの中心になるのは Task オブジェクトと Task<T> オブジェクトであり、非同期操作をモデル化します。The core of async programming is the Task and Task<T> objects, which model asynchronous operations. これらは、async および await キーワードによってサポートされています。They are supported by the async and await keywords. ほとんどの場合、モデルは非常に単純です。The model is fairly simple in most cases:

  • I/O バインドのコードでは、async メソッドの内部で、Task または Task<T> を返す操作を待機します。For I/O-bound code, you await an operation that returns a Task or Task<T> inside of an async method.
  • CPU バインドのコードでは、Task.Run メソッドによってバックグラウンド スレッドで開始された操作を待機します。For CPU-bound code, you await an operation that is started on a background thread with the Task.Run method.

await キーワードはマジックが行われる場所であり、The await keyword is where the magic happens. await を実行したメソッドの呼び出し元に制御が委譲されます。これによって最終的に、UI は応答できるようになり、サービスは柔軟性を持つようになります。It yields control to the caller of the method that performed await, and it ultimately allows a UI to be responsive or a service to be elastic. asyncawait 以外にも非同期コードを実現する方法はありますが、この記事では言語レベルのコンストラクトについて説明します。While there are ways to approach async code other than async and await, this article focuses on the language-level constructs.

I/O バインドの例: Web サービスからデータをダウンロードするI/O-bound example: Download data from a web service

ボタンがクリックされたら Web サービスからデータをダウンロードする必要がありますが UI スレッドはブロックしたくない、といった場合があります。You may need to download some data from a web service when a button is pressed, but don't want to block the UI thread. これは、次のようにして実現できます。It can be accomplished like this:

private readonly HttpClient _httpClient = new HttpClient();

downloadButton.Clicked += async (o, e) =>
    // This line will yield control to the UI as the request
    // from the web service is happening.
    // The UI thread is now free to perform other work.
    var stringData = await _httpClient.GetStringAsync(URL);

コードでは、Task オブジェクトとの対話に煩わされることなく意図すること (データを非同期的にダウンロードする) が表されています。The code expresses the intent (downloading data asynchronously) without getting bogged down in interacting with Task objects.

CPU バインドの例: ゲームの計算を実行するCPU-bound example: Perform a calculation for a game

ボタンをクリックすると画面上の多くの敵にダメージを与えることができるモバイル ゲームを作っています。Say you're writing a mobile game where pressing a button can inflict damage on many enemies on the screen. ダメージ計算の実行は負荷が大きく、UI スレッドで実行すると計算の実行中はゲームが停止しているように見えます。Performing the damage calculation can be expensive, and doing it on the UI thread would make the game appear to pause as the calculation is performed!

これを処理する最善の方法は、バックグラウンド スレッドを開始して、その中で Task.Run を使って処理を実行し、await を使用して結果を大気することです。The best way to handle this is to start a background thread, which does the work using Task.Run, and await its result using await. このようにすると、処理が行われている間も UI が停止することはありません。This allows the UI to feel smooth as the work is being done.

private DamageResult CalculateDamageDone()
    // Code omitted:
    // Does an expensive calculation and returns
    // the result of that calculation.

calculateButton.Clicked += async (o, e) =>
    // This line will yield control to the UI while CalculateDamageDone()
    // performs its work. The UI thread is free to perform other work.
    var damageResult = await Task.Run(() => CalculateDamageDone());

このコードでは、ボタンのクリック イベントの意図が明瞭に表されています。バックグラウンド スレッドを手動で管理する必要はなく、ブロッキングが発生しない方法で行われます。This code cleanly expresses the intent of the button's click event, it doesn't require managing a background thread manually, and it does so in a non-blocking way.

内部での処理What happens under the covers

非同期操作には多数の動作要素が関係します。There are many moving pieces where asynchronous operations are concerned. TaskTask<T> の内部処理について詳しくは、「非同期の詳細」をご覧ください。If you're curious about what's happening underneath the covers of Task and Task<T>, see the Async in-depth article for more information.

C# 側では、コンパイラによってコードがステート マシンに変換されます。ステート マシンでは、await に達したときの実行の委譲や、バックグラウンド ジョブが終了したときの実行の再開などが追跡されます。On the C# side of things, the compiler transforms your code into a state machine that keeps track of things like yielding execution when an await is reached and resuming execution when a background job has finished.

理論的には、これは非同期処理の約束モデルの実装です。For the theoretically-inclined, this is an implementation of the Promise Model of asynchrony.

理解すべき重要事項Key pieces to understand

  • 非同期コードは I/O バインドと CPU バインドの両方のコードで使うことができますが、使い方はシナリオにより異なります。Async code can be used for both I/O-bound and CPU-bound code, but differently for each scenario.
  • 非同期コードで使われる Task<T>Task は、バックグラウンドで行われる処理のモデル化に使われる構成要素です。Async code uses Task<T> and Task, which are constructs used to model work being done in the background.
  • キーワード async はメソッドを非同期メソッドに変換し、メソッドの本体で await キーワードを使用できるようにします。The async keyword turns a method into an async method, which allows you to use the await keyword in its body.
  • 適用された await キーワードは、呼び出しメソッドを中断し、待機中のタスクが完了するまで呼び出し元に制御を戻します。When the await keyword is applied, it suspends the calling method and yields control back to its caller until the awaited task is complete.
  • await は、非同期メソッドの中でのみ使用できます。await can only be used inside an async method.

CPU バインドと I/O バインドの処理Recognize CPU-bound and I/O-bound work

このガイドの最初の 2 つの例では、CPU バインドと I/O バインドの処理に対して async および await を使う方法を示しました。The first two examples of this guide showed how you can use async and await for I/O-bound and CPU-bound work. 行う必要のあるジョブが I/O バインドか CPU バインドかを識別できることが重要です。これは、コードのパフォーマンスに大きく影響し、特定の構成の誤った使用につながる可能性があります。It's key that you can identify when a job you need to do is I/O-bound or CPU-bound, because it can greatly affect the performance of your code and could potentially lead to misusing certain constructs.

コードを記述する前に次の 2 点について考える必要があります。Here are two questions you should ask before you write any code:

  1. コードは何か (データベースのデータなど) を "待機" していますか。Will your code be "waiting" for something, such as data from a database?

    答えが "はい" の場合、その処理は I/O バインドです。If your answer is "yes", then your work is I/O-bound.

  2. コードでは、負荷の大きい計算が実行されますか。Will your code be performing an expensive computation?

    答えが "はい" の場合、その処理は CPU バインドです。If you answered "yes", then your work is CPU-bound.

処理が I/O バインドの場合は、asyncawait を使いますが、Task.Run は "使いません"。If the work you have is I/O-bound, use async and await without Task.Run. タスク並列ライブラリは "使わないでください"。You should not use the Task Parallel Library. その理由について詳しくは、「非同期の詳細」をご覧ください。The reason for this is outlined in Async in Depth.

処理が CPU バインドであり、応答性が重要な場合は、asyncawait を使い、Task.Run を "使って" 別のスレッドで処理を実行します。If the work you have is CPU-bound and you care about responsiveness, use async and await, but spawn off the work on another thread with Task.Run. 処理がコンカレンシーと並列処理に適している場合は、タスク並列ライブラリを使うことも考慮します。If the work is appropriate for concurrency and parallelism, also consider using the Task Parallel Library.

さらに、常にコードの実行を測定する必要があります。Additionally, you should always measure the execution of your code. たとえば、マルチスレッドでのコンテキスト切り替えのオーバーヘッドと比較して、CPU バインドの処理の負荷がそれほど大きくないことがわかる場合があります。For example, you may find yourself in a situation where your CPU-bound work is not costly enough compared with the overhead of context switches when multithreading. すべての選択肢にはトレードオフがあり、状況に合った適切なトレードオフを選ぶ必要があります。Every choice has its tradeoff, and you should pick the correct tradeoff for your situation.

その他の例More examples

以下では、C# で非同期コードを記述するさまざまな方法がわかる例を示します。The following examples demonstrate various ways you can write async code in C#. 実際に遭遇する可能性があるいくつかの異なるシナリオを使います。They cover a few different scenarios you may come across.

ネットワークからデータを抽出するExtract data from a network

このスニペットでは、https://dotnetfoundation.org にあるホームページから HTML がダウンロードされ、HTML に文字列 ".NET" が出現する回数が数えられます。This snippet downloads the HTML from the homepage at https://dotnetfoundation.org and counts the number of times the string ".NET" occurs in the HTML. ASP.NET を使用して Web API コントローラー メソッドが定義され、そのメソッドによってこのタスクが実行されて、値が返されます。It uses ASP.NET to define a Web API controller method, which performs this task and returns the number.


運用コードで HTML の解析の実行を計画している場合は、正規表現を使用しないでください。If you plan on doing HTML parsing in production code, don't use regular expressions. 代わりに解析ライブラリを使用します。Use a parsing library instead.

private readonly HttpClient _httpClient = new HttpClient();

[HttpGet, Route("DotNetCount")]
public async Task<int> GetDotNetCount()
    // Suspends GetDotNetCount() to allow the caller (the web server)
    // to accept another request, rather than blocking on this one.
    var html = await _httpClient.GetStringAsync("https://dotnetfoundation.org");

    return Regex.Matches(html, @"\.NET").Count;

次に示すのはユニバーサル Windows アプリ用に記述された同じシナリオであり、ボタンがクリックされたら同じタスクを実行します。Here's the same scenario written for a Universal Windows App, which performs the same task when a Button is pressed:

private readonly HttpClient _httpClient = new HttpClient();

private async void OnSeeTheDotNetsButtonClick(object sender, RoutedEventArgs e)
    // Capture the task handle here so we can await the background task later.
    var getDotNetFoundationHtmlTask = _httpClient.GetStringAsync("https://dotnetfoundation.org");

    // Any other work on the UI thread can be done here, such as enabling a Progress Bar.
    // This is important to do here, before the "await" call, so that the user
    // sees the progress bar before execution of this method is yielded.
    NetworkProgressBar.IsEnabled = true;
    NetworkProgressBar.Visibility = Visibility.Visible;

    // The await operator suspends OnSeeTheDotNetsButtonClick(), returning control to its caller.
    // This is what allows the app to be responsive and not block the UI thread.
    var html = await getDotNetFoundationHtmlTask;
    int count = Regex.Matches(html, @"\.NET").Count;

    DotNetCountLabel.Text = $"Number of .NETs on dotnetfoundation.org: {count}";

    NetworkProgressBar.IsEnabled = false;
    NetworkProgressBar.Visibility = Visibility.Collapsed;

複数タスクの完了を待機するWait for multiple tasks to complete

複数のデータを同時に取得することが必要になる場合があります。You may find yourself in a situation where you need to retrieve multiple pieces of data concurrently. Task API には 2 つのメソッド Task.WhenAllTask.WhenAny が含まれており、これらを使用して、複数のバックグラウンド ジョブで非ブロッキング待機を実行する非同期コードを記述できます。The Task API contains two methods, Task.WhenAll and Task.WhenAny, that allow you to write asynchronous code that performs a non-blocking wait on multiple background jobs.

次の例では、一連の userId に対する User データを取得する方法を示します。This example shows how you might grab User data for a set of userIds.

public async Task<User> GetUserAsync(int userId)
    // Code omitted:
    // Given a user Id {userId}, retrieves a User object corresponding
    // to the entry in the database with {userId} as its Id.

public static async Task<IEnumerable<User>> GetUsersAsync(IEnumerable<int> userIds)
    var getUserTasks = new List<Task<User>>();
    foreach (int userId in userIds)

    return await Task.WhenAll(getUserTasks);

LINQ を使ってさらに簡潔に記述する別の方法を次に示します。Here's another way to write this more succinctly, using LINQ:

public async Task<User> GetUserAsync(int userId)
    // Code omitted:
    // Given a user Id {userId}, retrieves a User object corresponding
    // to the entry in the database with {userId} as its Id.

public static async Task<User[]> GetUsersAsync(IEnumerable<int> userIds)
    var getUserTasks = userIds.Select(id => GetUserAsync(id));
    return await Task.WhenAll(getUserTasks);

コードは少ないですが、LINQ と非同期コードを併用するときは注意してください。Although it's less code, use caution when mixing LINQ with asynchronous code. LINQ は遅延実行を使うので、生成されたシーケンスを .ToList() または .ToArray() の呼び出しで強制的に反復処理させない限り、foreach ループ内で行われた非同期呼び出しはすぐに実行されません。Because LINQ uses deferred (lazy) execution, async calls won't happen immediately as they do in a foreach loop unless you force the generated sequence to iterate with a call to .ToList() or .ToArray().

重要な情報とアドバイスImportant info and advice

非同期プログラミングには、留意することで予期しない動作を防ぐことができる細かい事柄がいくつかあります。With async programming there are some details to keep in mind which can prevent unexpected behavior.

  • async メソッドの本体に await キーワードが含まれないと、何も行われません。async methods need to have an await keyword in their body or they will never yield!

    これは、忘れてはならない重要なことです。This is important to keep in mind. awaitasync メソッドの本体で使用されていない場合、C# コンパイラでは警告が生成されますが、コードは通常のメソッドと同様にコンパイルされて実行されます。If await is not used in the body of an async method, the C# compiler generates a warning, but the code compiles and runs as if it were a normal method. 非同期メソッドに対して C# コンパイラによって生成されるステート マシンでは何も行われないため、これは非常に非効率的です。This is incredibly inefficient, as the state machine generated by the C# compiler for the async method is not accomplishing anything.

  • 記述するすべての非同期メソッド名のサフィックスとして、"Async" を追加する必要がありますYou should add "Async" as the suffix of every async method name you write.

これは、同期メソッドと非同期メソッドの区別をより簡単にするために、.NET で使われる規則です。This is the convention used in .NET to more easily differentiate synchronous and asynchronous methods. コードによって明示的に呼び出されない一部のメソッド (イベント ハンドラーや Web コントローラー メソッドなど) には、必ずしも当てはまりません。Certain methods that aren't explicitly called by your code (such as event handlers or web controller methods) don't necessarily apply. そのようなメソッドはコードでは明示的に呼び出されないため、明示的な命名はそれほど重要ではありません。Because they are not explicitly called by your code, being explicit about their naming isn't as important.

  • async void はイベント ハンドラーに対してのみ使う必要があります。async void should only be used for event handlers.

イベントには戻り値の型がないため、async void は非同期イベント ハンドラーの動作を可能にする唯一の方法です (したがって、TaskTask<T> を使うことはできません)。async void is the only way to allow asynchronous event handlers to work because events do not have return types (thus cannot make use of Task and Task<T>). async void のその他の使用はすべて TAP モデルに従わないので、使うのが難しい場合があります。以下はその例です。Any other use of async void does not follow the TAP model and can be challenging to use, such as:

  • async void メソッドでスローされた例外を、そのメソッドの外部でキャッチすることはできません。Exceptions thrown in an async void method can't be caught outside of that method.

  • async void メソッドをテストするのは困難です。async void methods are difficult to test.

  • async void メソッドでは、呼び出し元がそれを非同期と予期していないと、悪い副作用が発生する可能性があります。async void methods can cause bad side effects if the caller isn't expecting them to be async.

  • LINQ 式での非同期ラムダの使用は慎重に行う必要がありますTread carefully when using async lambdas in LINQ expressions

LINQ 内のラムダ式では、遅延実行が使用されます。つまり、予期していないときにコードが実行される可能性があります。Lambda expressions in LINQ use deferred execution, meaning code could end up executing at a time when you're not expecting it to. これにブロッキング タスクを組み込んだ場合、正しく作成されていないと、簡単にデッドロックが発生します。The introduction of blocking tasks into this can easily result in a deadlock if not written correctly. さらに、このように非同期コードを入れ子にすると、コードの実行についての推論も難しくなります。Additionally, the nesting of asynchronous code like this can also make it more difficult to reason about the execution of the code. 非同期と LINQ は強力ですが、併用するときは可能な限り慎重かつ明確にする必要があります。Async and LINQ are powerful, but should be used together as carefully and clearly as possible.

  • タスクを待機するコードは非ブロッキング方式で作成しますWrite code that awaits Tasks in a non-blocking manner

Task が完了するのを待機するための手段として現在のスレッドをブロックすると、デッドロックおよびコンテキスト スレッドのブロックが発生する可能性があり、複雑なエラー処理が必要になることがあります。Blocking the current thread as a means to wait for a Task to complete can result in deadlocks and blocked context threads, and can require more complex error-handling. 次の表では、非ブロッキング方式でタスクの待機に対処する方法について説明します。The following table provides guidance on how to deal with waiting for tasks in a non-blocking way:

これを使うUse this... これは使わないInstead of this... 行う処理When wishing to do this...
await Task.Wait または Task.ResultTask.Wait or Task.Result バックグラウンド タスクの結果の取得Retrieving the result of a background task
await Task.WhenAny Task.WaitAny 任意のタスクの完了の待機Waiting for any task to complete
await Task.WhenAll Task.WaitAll すべてのタスクの完了の待機Waiting for all tasks to complete
await Task.Delay Thread.Sleep 一定期間の待機Waiting for a period of time
  • 可能な場合は ValueTask の使用を検討するConsider using ValueTask where possible

非同期メソッドから Task オブジェクトを返すと、特定のパスでパフォーマンスのボトルネックが発生する可能性があります。Returning a Task object from async methods can introduce performance bottlenecks in certain paths. Task は参照型です。したがって、これを使うことは、オブジェクトを割り当てることを意味します。Task is a reference type, so using it means allocating an object. async 修飾子で宣言されたメソッドがキャッシュされた結果を返すか、同期的に完了する場合、追加の割り当ては、コードのパフォーマンスが重要なセクションにおいて大きな時間コストにつながります。In cases where a method declared with the async modifier returns a cached result or completes synchronously, the extra allocations can become a significant time cost in performance critical sections of code. 厳密なループ処理でこのような割り当てが発生した場合、コストがかかる場合があります。It can become costly if those allocations occur in tight loops. 詳しくは、「一般化された async の戻り値の型」をご覧ください。For more information, see generalized async return types.

  • ConfigureAwait(false) の使用を検討するConsider using ConfigureAwait(false)

"どのような場合に Task.ConfigureAwait(Boolean) メソッドを使用する必要があるか" ということが問題になることがよくあります。A common question is, "when should I use the Task.ConfigureAwait(Boolean) method?". このメソッドを使用すると、Task インスタンスでその awaiter を構成できます。The method allows for a Task instance to configure its awaiter. これは重要な考慮事項であり、それを正しく設定しないと、パフォーマンスに影響し、デッドロックが発生する可能性があります。This is an important consideration and setting it incorrectly could potentially have performance implications and even deadlocks. ConfigureAwait の詳細については、「ConfigureAwait の FAQ」を参照してください。For more information on ConfigureAwait, see the ConfigureAwait FAQ.

  • ステートフル性の低いコードを記述しますWrite less stateful code

グローバル オブジェクトの状態または特定のメソッドの実行に依存しないでください。Don't depend on the state of global objects or the execution of certain methods. 代わりに、メソッドの戻り値のみに依存するようにします。Instead, depend only on the return values of methods. なぜでしょうか。Why?

  • コードを理解しやすくなります。Code will be easier to reason about.
  • コードをテストしやすくなります。Code will be easier to test.
  • 非同期コードと同期コードの混在がはるかに簡単になります。Mixing async and synchronous code is far simpler.
  • 一般には、競合状態を完全に回避できます。Race conditions can typically be avoided altogether.
  • 戻り値に依存すると、非同期コードの調整が簡単になります。Depending on return values makes coordinating async code simple.
  • (ボーナス) 依存関係の挿入で問題なく動作します。(Bonus) it works really well with dependency injection.

推奨される目標は、完全またはほぼ完全な参照の透過性をコードで実現することです。A recommended goal is to achieve complete or near-complete Referential Transparency in your code. そうすることで、予測、テスト、保守が非常に容易なコードベースになります。Doing so will result in an extremely predictable, testable, and maintainable codebase.

その他のリソースOther resources