イベントベースの非同期パターンを実装するための推奨される手順

イベントベースの非同期パターンは、使い慣れたイベントおよびデリゲートのセマンティクスと共に、クラス内で非同期動作を公開する効果的な方法を提供します。 イベントベースの非同期パターンを実装するには、動作上の特定の要件を満たす必要があります。 以降のセクションでは、イベントベースの非同期パターンに準拠するクラスを実装する際に検討する必要のある要件およびガイドラインについて説明します。

概要については、「イベントベースの非同期パターンの実装」を参照してください。

このトピックで説明する推奨される手順を次に示します。

  • 必要な動作保障

  • 完了

  • 完了したイベントおよび EventArgs

  • 同時に実行する操作

  • 結果へのアクセス

  • 進行状況のレポート

  • IsBusy の実装

  • キャンセル

  • エラーおよび例外

  • スレッド処理およびコンテキスト

  • ガイドライン

必要な動作保障

イベントベースの非同期パターンを実装する場合は、多数の保障を提供して、クラスが適切に動作し、クラスのクライアントがそのような動作に依存できるようにする必要があります。

完了

正常な完了、エラー、キャンセルの際には、常に MethodNameCompleted イベント ハンドラーを呼び出します。 アプリケーションでは、アイドル状態のままで完了しないような状況が発生するべきではありません。 このルールの唯一の例外は、非同期操作そのものが完了しないようにデザインされている場合です。

完了したイベントおよび EventArgs

個々の MethodNameAsync メソッドごとに、次のデザイン要件を適用します。

  • メソッドと同じクラス内で、MethodNameCompleted イベントを定義します。

  • AsyncCompletedEventArgsクラスから派生した MethodNameCompleted イベントの、EventArgs クラスおよび付随するデリゲートを定義します。 既定のクラス名は、MethodNameCompletedEventArgs という形式である必要があります。

  • EventArgs クラスは、MethodName メソッドの戻り値に固有である必要があります。 EventArgs クラスを使用すると、開発者に結果をキャストさせる必要がなくなります。

    次のコード例に、このデザイン要求の良い実装と悪い実装をそれぞれ示します。

[C#]

// Good design
private void Form1_MethodNameCompleted(object sender, xxxCompletedEventArgs e) 
{ 
    DemoType result = e.Result;
}

// Bad design
private void Form1_MethodNameCompleted(object sender, MethodNameCompletedEventArgs e) 
{ 
    DemoType result = (DemoType)(e.Result);
}
  • void を返すメソッドを返すための EventArgs クラスは定義しないでください。 代わりに、AsyncCompletedEventArgs クラスのインスタンスを使用してください。

  • 常に MethodNameCompleted イベントを発生させる必要があります。 このイベントは、正常完了時、エラー時、または取り消し時に発生させる必要があります。 アプリケーションでは、アイドル状態のままで完了しないような状況が発生するべきではありません。

  • 非同期操作で発生した例外をキャッチし、キャッチした例外を Error プロパティに割り当てる必要があります。

  • タスクの実行中にエラーが発生した場合は、結果にアクセスできません。 Error プロパティが null ではない場合に EventArgs 構造体のプロパティにアクセスしたときは例外が発生するようにします。 この検証を行うには、RaiseExceptionIfNecessary メソッドを使用します。

  • タイムアウトをエラーとしてモデル化します。 タイムアウトが発生した場合は、MethodNameCompleted イベントを発生させ、TimeoutExceptionError プロパティに割り当てます。

  • クラスが複数の同時呼び出しをサポートする場合は、MethodNameCompleted イベントに適切な userSuppliedState オブジェクトを含める必要があります。

  • MethodNameCompleted イベントが、適切なスレッドで、アプリケーションの有効期間内の適切な時期に発生するようにします。 詳細については、「スレッド処理およびコンテキスト」のセクションを参照してください。

同時に実行する操作

  • クラスが複数の同時呼び出しをサポートする場合、開発者が各呼び出しを個別に追跡できるようにするには、userSuppliedState という名前のオブジェクト値の状態パラメーター (タスク ID) を受け取る MethodNameAsync オーバーロードを定義します。 このパラメーターは、MethodNameAsync メソッドのシグネチャ内で常に最後のパラメーターにする必要があります。

  • クラスが値の状態パラメーター (タスク ID) を受け取る MethodNameAsync オーバーロードを定義する場合は、操作の有効期間をそのタスク ID で追跡し、それを完了ハンドラーに戻す必要があります。 役に立つヘルパー クラスがあります。 同時実行の管理の詳細については、「チュートリアル : イベントベースの非同期パターンをサポートするコンポーネントの実装」を参照してください。

  • クラスが状態パラメーターなしで MethodNameAsync メソッドを定義し、複数の同時呼び出しをサポートしない場合は、直前の MethodNameAsync の呼び出しが完了する前に MethodNameAsync を呼び出そうとすると、InvalidOperationException が発生するようにします。

  • 通常は、MethodNameAsync メソッドが userSuppliedState パラメーターなしで複数回呼び出される場合は、複数の未完了の操作が存在しないように、例外を発生させないでください。 例外は、クラスがその状況を処理できないことが明らかで、開発者ならこれらの複数の区別が付かないコールバックを処理できる場合に発生させます。

結果へのアクセス

進行状況のレポート

  • 可能な場合は進行状況のレポートをサポートします。 こうすると、開発者はクラスを使用して、より良いアプリケーション ユーザー環境を提供できます。

  • ProgressChanged/MethodNameProgressChanged イベントを実装する場合は、その操作の MethodNameCompleted イベントが発生した後で、特定の非同期操作についてそのようなイベントが発生しないようにします。

  • 標準の ProgressChangedEventArgs が生成された場合は、ProgressPercentage が常にパーセンテージとして解釈されるようにします。 パーセンテージは、正確でなくてもかまいませんが、パーセンテージを表している必要があります。 進行状況レポートのメトリックがパーセンテージ以外の場合は、ProgressChangedEventArgs クラスからクラスを派生させ、ProgressPercentage は 0 にします。 パーセンテージ以外のレポート メトリックは使用しないでください。

  • ProgressChanged イベントが、適切なスレッドで、アプリケーションの有効期間内の適切な時期に発生するようにします。 詳細については、「スレッド処理およびコンテキスト」のセクションを参照してください。

IsBusy の実装

  • クラスが複数の同時呼び出しをサポートする場合は、IsBusy プロパティを公開しないでください。 たとえば、XML Web サービス プロキシは、非同期メソッドの複数の同時呼び出しをサポートするため、IsBusy プロパティを公開しません。

  • IsBusy プロパティは、MethodNameAsync メソッドが呼び出された後、MethodNameCompleted イベントが発生する前に true を返す必要があります。 それ以外の場合は、false を返します。 BackgroundWorker および WebClient コンポーネントは、IsBusy プロパティを公開するクラスの例です。

キャンセル

  • できればキャンセルをサポートします。 こうすると、開発者はクラスを使用して、より良いアプリケーション ユーザー環境を提供できます。

  • キャンセルの場合は、AsyncCompletedEventArgs オブジェクトで Cancelled フラグを設定します。

  • 結果にアクセスしようとしたときに、操作がキャンセルされたことを示す InvalidOperationException を発生させてください。 この検証を行うには、AsyncCompletedEventArgs.RaiseExceptionIfNecessary メソッドを使用します。

  • キャンセル メソッドの呼び出しは、常に正常に返し、例外を発生させないようにする必要があります。 通常、クライアントには、操作が特定の時間に実際にキャンセル可能かどうかは通知されず、直前に実行されたキャンセルが成功したかどうかは通知されません。 ただし、アプリケーションには、キャンセルが成功したときに常に通知されます。これは、アプリケーションが完了ステータスの一部だからです。

  • 操作がキャンセルされたときに、MethodNameCompleted イベントを発生させます。

エラーおよび例外

  • 非同期操作で発生した任意の例外を受け取り、AsyncCompletedEventArgs.Error プロパティの値をその例外に設定します。

スレッド処理およびコンテキスト

クラスを適切に動作させるには、特定のアプリケーション モデル (ASP.NET や Windows フォーム アプリケーションなど) の適切なスレッドまたはコンテキストで、クライアントのイベント ハンドラーが呼び出されることが重要です。 非同期クラスが任意のアプリケーション モデルで適切に動作するように、AsyncOperationAsyncOperationManager の 2 つの重要なヘルパー クラスが提供されています。

AsyncOperationManager は片方のメソッド、CreateOperation を提供します。これは、AsyncOperation を返します。 MethodNameAsync メソッドは CreateOperation を呼び出し、クラスは返された AsyncOperation を使用して、非同期タスクの有効期間を追跡します。

進行状況、インクリメンタル結果、および完了をクライアントにレポートするには、Post および OperationCompleted メソッドを AsyncOperation で呼び出します。 AsyncOperation は、クライアントのイベント ハンドラーの呼び出しを、適切なスレッドまたはコンテキストにマーシャリングします。

メモメモ

アプリケーション モデルのポリシーに明示的に逆らいつつ、イベントベースの非同期パターンの使用によるその他の利点は活用し続ける場合には、これらのルールを回避できます。たとえば、Windows フォームで動作するクラスをフリー スレッドにするとします。開発者が暗黙の制限を把握している限り、フリー スレッドのクラスを作成できます。コンソール アプリケーションでは、Post 呼び出しの実行は同期化されません。これが原因で、ProgressChanged イベントが異常な順序で発生する可能性があります。Post 呼び出しの実行をシリアル化する場合は、System.Threading.SynchronizationContext クラスを実装およびインストールします。

AsyncOperation および AsyncOperationManager を使用して、非同期操作を可能にする方法の詳細については、「チュートリアル : イベントベースの非同期パターンをサポートするコンポーネントの実装」を参照してください。

ガイドライン

  • 各メソッドの呼び出しは、他と独立しているのが理想です。 共有リソースとの呼び出しの結合は避けます。 リソースを複数の呼び出しの間で共有する場合は、適切な同期機構を実装に提供する必要があります。

  • クライアントに同期を実装する必要のあるデザインはお勧めしません。 たとえば、グローバルな静的オブジェクトをパラメーターとして受け取る非同期メソッドを使用できます。そのようなメソッドを同時に複数呼び出すと、データが破壊され、デッドロック状態になる場合があります。

  • 複数呼び出しのオーバーロード (userState シグネチャ内) でメソッドを実装する場合、クラスはユーザーの状態 (タスク ID) および対応する保留中の操作のコレクションを管理する必要があります。 このコレクションは、lock 領域で保護する必要があります。さまざまな呼び出しによって、コレクション内の userState オブジェクトが追加および削除されるからです。

  • 可能で適切な場合は、CompletedEventArgs クラスの再利用を検討してください。 この場合、特定のデリゲートおよび EventArgs の型は単一のメソッドに結び付けられないため、名前付けはメソッド名と一致しません。 ただし、EventArgs のプロパティから取得した値を開発者にキャストさせることはできません。

  • Component から派生するクラスを記述する場合は、独自の SynchronizationContext クラスを実装およびインストールしないでください。 使用される SynchronizationContext を制御するのは、コンポーネントではなく、アプリケーション モデルです。

  • 任意の種類のマルチスレッド を使用すると、非常に深刻で複雑なバグに対して自身を公開する可能性があります。 マルチスレッドを使用しているソリューションを実装する前に、「マネージ スレッド処理の実施」を参照してください。

参照

処理手順

方法 : イベントベースの非同期パターンをサポートするコンポーネントを使用する

チュートリアル : イベントベースの非同期パターンをサポートするコンポーネントの実装

参照

AsyncOperation

AsyncOperationManager

AsyncCompletedEventArgs

ProgressChangedEventArgs

BackgroundWorker

概念

イベントベースの非同期パターンの実装

イベントベースの非同期パターンをいつ実装するかの決定

イベントベースの非同期パターンを実装するための推奨される手順

その他の技術情報

イベント ベースの非同期パターンを使用したマルチスレッド プログラミング