多くのタスクを同時に実行するが、ユーザーの操作に対する応答性を維持するアプリケーションには、多くの場合、複数のスレッドを使用する設計が必要です。 System.Threading名前空間には、高パフォーマンスのマルチスレッド アプリケーションを作成するために必要なすべてのツールが用意されていますが、これらのツールを効果的に使用するには、マルチスレッド ソフトウェア エンジニアリングに関する重要な経験が必要です。 比較的単純なマルチスレッド アプリケーションの場合、 BackgroundWorker コンポーネントは簡単なソリューションを提供します。 より高度な非同期アプリケーションの場合は、イベント ベースの非同期パターンに準拠するクラスを実装することを検討してください。
イベント ベースの非同期パターンを使用すると、マルチスレッド 設計に固有の複雑な問題の多くを非表示にしながら、マルチスレッド アプリケーションの利点を利用できます。 このパターンをサポートするクラスを使用すると、次のことができます。
アプリケーションを中断することなく、"バックグラウンドで" ダウンロードやデータベース操作などの時間のかかるタスクを実行します。
複数の操作を同時に実行し、各操作が完了したときに通知を受け取ります。
アプリケーションを停止 ("ブロック") することなく、リソースが使用可能になるまで待ちます。
使い慣れたイベントとデリゲート のモデルを使用して、保留中の非同期操作と通信します。 イベント ハンドラーとデリゲートの使用の詳細については、「 イベント」を参照してください。
イベント ベースの非同期パターンをサポートするクラスには、 MethodNameAsync という名前の 1 つ以上のメソッドがあります。 これらのメソッドは、現在のスレッドで同じ操作を実行する同期バージョンをミラーリングできます。 クラスには MethodNameCompleted イベントがあり、 MethodNameAsyncCancel (または単に CancelAsync) メソッドを持つ場合もあります。
PictureBox は、イベント ベースの非同期パターンをサポートする一般的なコンポーネントです。 イメージを同期的にダウンロードするには、 Load メソッドを呼び出しますが、イメージが大きい場合、またはネットワーク接続が遅い場合は、ダウンロード操作が完了し、 Load の呼び出しが返されるまで、アプリケーションの応答が停止します。
イメージの読み込み中にアプリケーションを実行し続ける場合は、他のイベントを処理する場合と同様に、 LoadAsync メソッドを呼び出し、 LoadCompleted イベントを処理できます。 LoadAsync メソッドを呼び出すと、ダウンロードが別のスレッド ("バックグラウンド") で続行されている間、アプリケーションは引き続き実行されます。 イメージの読み込み操作が完了するとイベント ハンドラーが呼び出され、イベント ハンドラーは AsyncCompletedEventArgs パラメーターを調べて、ダウンロードが正常に完了したかどうかを判断できます。
イベント ベースの非同期パターンでは、非同期操作を取り消すことができる必要があります。 PictureBox コントロールは、 CancelAsync メソッドでこの要件をサポートします。 CancelAsyncを呼び出すと、保留中のダウンロードを停止する要求が送信され、タスクが取り消されると、LoadCompleted イベントが発生します。
注意事項
CancelAsync要求が行われるのと同じようにダウンロードが完了する可能性があるため、Cancelledはキャンセル要求を反映していない可能性があります。 これは 競合状態 と呼ばれ、マルチスレッド プログラミングの一般的な問題です。 マルチスレッド プログラミングの問題の詳細については、「 マネージド スレッドのベスト プラクティス」を参照してください。
イベント ベースの非同期パターンの特性
イベント ベースの非同期パターンは、特定のクラスでサポートされる操作の複雑さに応じて、いくつかの形式を使用できます。 最も単純なクラスには、1 つの MethodNameAsync メソッドと、対応する MethodNameCompleted イベントがあります。 より複雑なクラスには、複数の MethodNameAsync メソッドがあり、それぞれに対応する MethodNameCompleted イベントと、これらのメソッドの同期バージョンがあります。 クラスでは、必要に応じて、各非同期メソッドのキャンセル、進行状況レポート、増分結果をサポートできます。
非同期メソッドは、複数の保留中の呼び出し (複数の同時呼び出し) をサポートする場合もあります。これにより、コードは、他の保留中の操作を完了する前に任意の回数呼び出すことができます。 この状況を正しく処理するには、アプリケーションで各操作の完了を追跡する必要がある場合があります。
イベント ベースの非同期パターンの例
SoundPlayerコンポーネントとPictureBox コンポーネントは、イベント ベースの非同期パターンの単純な実装を表します。 WebClientおよびBackgroundWorkerコンポーネントは、イベント ベースの非同期パターンのより複雑な実装を表します。
パターンに準拠するクラス宣言の例を次に示します。
Public Class AsyncExample
' Synchronous methods.
Public Function Method1(ByVal param As String) As Integer
Public Sub Method2(ByVal param As Double)
' Asynchronous methods.
Overloads Public Sub Method1Async(ByVal param As String)
Overloads Public Sub Method1Async(ByVal param As String, ByVal userState As Object)
Public Event Method1Completed As Method1CompletedEventHandler
Overloads Public Sub Method2Async(ByVal param As Double)
Overloads Public Sub Method2Async(ByVal param As Double, ByVal userState As Object)
Public Event Method2Completed As Method2CompletedEventHandler
Public Sub CancelAsync(ByVal userState As Object)
Public ReadOnly Property IsBusy () As Boolean
' Class implementation not shown.
End Class
public class AsyncExample
{
// Synchronous methods.
public int Method1(string param);
public void Method2(double param);
// Asynchronous methods.
public void Method1Async(string param);
public void Method1Async(string param, object userState);
public event Method1CompletedEventHandler Method1Completed;
public void Method2Async(double param);
public void Method2Async(double param, object userState);
public event Method2CompletedEventHandler Method2Completed;
public void CancelAsync(object userState);
public bool IsBusy { get; }
// Class implementation not shown.
}
架空の AsyncExample
クラスには、同期呼び出しと非同期呼び出しの両方をサポートする 2 つのメソッドがあります。 同期オーバーロードは、任意のメソッド呼び出しと同様に動作し、呼び出し元のスレッドで操作を実行します。操作に時間がかかる場合は、呼び出しが戻るまでに顕著な遅延が発生する可能性があります。 非同期オーバーロードは、別のスレッドで操作を開始し、すぐに戻り、操作が "バックグラウンドで" 実行されている間、呼び出し元のスレッドを続行できるようにします。
非同期メソッドのオーバーロード
非同期操作には、単一呼び出しと複数呼び出しの 2 つのオーバーロードが存在する可能性があります。 これらの 2 つのフォームは、メソッド シグネチャによって区別できます。複数呼び出しフォームには、 userState
という追加のパラメーターがあります。 このフォームを使用すると、保留中の非同期操作が完了するのを待たずに、コードで Method1Async(string param, object userState)
を複数回呼び出すことができます。 一方、前の呼び出しが完了する前に Method1Async(string param)
を呼び出そうとすると、メソッドは InvalidOperationExceptionを発生させます。
複数呼び出しオーバーロードの userState
パラメーターを使用すると、非同期操作を区別できます。
Method1Async(string param, object userState)
の呼び出しごとに一意の値 (GUID やハッシュ コードなど) を指定し、各操作が完了すると、イベント ハンドラーは操作のどのインスタンスが完了イベントを発生させたかを判断できます。
保留中の操作の追跡
複数呼び出しのオーバーロードを使用する場合、コードは保留中のタスクの userState
オブジェクト (タスク ID) を追跡する必要があります。
Method1Async(string param, object userState)
の呼び出しごとに、通常は新しい一意のuserState
オブジェクトを生成し、コレクションに追加します。 この userState
オブジェクトに対応するタスクが完了イベントを発生させると、完了メソッドの実装によって AsyncCompletedEventArgs.UserState が調べられ、コレクションから削除されます。 この方法を使用すると、 userState
パラメーターはタスク ID のロールを受け取ります。
注
複数呼び出しオーバーロードの呼び出しで userState
に一意の値を指定するように注意する必要があります。 一意でないタスク ID を指定すると、非同期クラスが ArgumentExceptionをスローする原因となります。
保留中の操作の取り消し
非同期操作を完了する前にいつでもキャンセルできることが重要です。 イベント ベースの非同期パターンを実装するクラスには、 CancelAsync
メソッド (非同期メソッドが 1 つしかない場合) または MethodNameAsyncCancel メソッド (複数の非同期メソッドがある場合) があります。
複数の呼び出しを許可するメソッドは、各タスクの有効期間を追跡するために使用できる userState
パラメーターを受け取ります。
CancelAsync
は userState
パラメーターを受け取ります。これにより、特定の保留中のタスクを取り消すことができます。
Method1Async(string param)
など、保留中の操作を一度に 1 つだけサポートするメソッドはキャンセルできません。
進行状況の更新と逐次的な結果の受信
イベント ベースの非同期パターンに準拠するクラスは、必要に応じて進行状況と増分結果を追跡するためのイベントを提供できます。 これは通常、 ProgressChanged
または MethodNameProgressChanged という名前になり、対応するイベント ハンドラーは ProgressChangedEventArgs パラメーターを受け取ります。
ProgressChanged
イベントのイベント ハンドラーは、ProgressChangedEventArgs.ProgressPercentage プロパティを調べて、非同期タスクが完了した割合を判断できます。 このプロパティの範囲は 0 から 100 で、ValueのProgressBar プロパティを更新するために使用できます。 複数の非同期操作が保留中の場合は、 ProgressChangedEventArgs.UserState プロパティを使用して、進行状況を報告している操作を識別できます。
一部のクラスでは、非同期操作の進行に応じて増分結果が報告される場合があります。 これらの結果は、 ProgressChangedEventArgs から派生したクラスに格納され、派生クラスのプロパティとして表示されます。
ProgressChanged
プロパティにアクセスする場合と同様に、ProgressPercentage イベントのイベント ハンドラーでこれらの結果にアクセスできます。 複数の非同期操作が保留中の場合は、 UserState プロパティを使用して、増分結果を報告している操作を識別できます。
こちらも参照ください
.NET