イベントベースの非同期パターンを実装するための推奨される手順
更新 : 2007 年 11 月
イベントベースの非同期パターンは、使い慣れたイベントおよびデリゲートのセマンティクスと共に、クラス内で非同期動作を公開する効果的な方法を提供します。イベントベースの非同期パターンを実装するには、動作上の特定の要件を満たす必要があります。以降のセクションでは、イベントベースの非同期パターンに準拠するクラスを実装する際に検討する必要のある要件およびガイドラインについて説明します。
概要については、「イベントベースの非同期パターンの実装」を参照してください。
このトピックで説明する推奨される手順を次に示します。
必要な動作保障
完了
完了したイベントおよび 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);
}
同時に実行する操作
クラスが複数の同時呼び出しをサポートする場合、開発者が各呼び出しを個別に追跡できるようにするには、userSuppliedState という名前のオブジェクト値の状態パラメータ (タスク ID) を受け取る MethodNameAsync オーバーロードを定義します。このパラメータは、MethodNameAsync メソッドのシグネチャ内で常に最後のパラメータにする必要があります。
クラスが値の状態パラメータ (タスク ID) を受け取る MethodNameAsync オーバーロードを定義する場合は、操作の有効期間をそのタスク ID で追跡し、それを完了ハンドラに戻す必要があります。役に立つヘルパー クラスがあります。同時実行の管理の詳細については、「チュートリアル : イベントベースの非同期パターンをサポートするコンポーネントの実装」を参照してください。
クラスが状態パラメータなしで MethodNameAsync メソッドを定義し、複数の同時呼び出しをサポートしない場合は、直前の MethodNameAsync の呼び出しが完了する前に MethodNameAsync を呼び出そうとすると、InvalidOperationException が発生するようにします。
通常は、MethodNameAsync メソッドが userSuppliedState パラメータなしで複数回呼び出される場合は、複数の未完了の操作が存在しないように、例外を発生させないでください。例外は、クラスがその状況を処理できないことが明らかで、開発者ならこれらの複数の区別が付かないコールバックを処理できる場合に発生させます。
結果へのアクセス
非同期操作の実行中にエラーが発生した場合は、結果にアクセスできません。Error が null ではないときに AsyncCompletedEventArgs のプロパティにアクセスした場合、Error によって参照される例外を発生させてください。AsyncCompletedEventArgs クラスは、この目的で RaiseExceptionIfNecessary メソッドを提供します。
結果にアクセスしようとしたときに、操作がキャンセルされたことを示す InvalidOperationException を発生させてください。この検証を行うには、AsyncCompletedEventArgs.RaiseExceptionIfNecessary メソッドを使用します。
進行状況のレポート
可能な場合は進行状況のレポートをサポートします。こうすると、開発者はクラスを使用して、より良いアプリケーション ユーザー環境を提供できます。
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 フォーム アプリケーションなど) の適切なスレッドまたはコンテキストで、クライアントのイベント ハンドラが呼び出されることが重要です。非同期クラスが任意のアプリケーション モデルで適切に動作するように、AsyncOperation と AsyncOperationManager の 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 を制御するのは、コンポーネントではなく、アプリケーション モデルです。
任意の種類のマルチスレッド を使用すると、非常に深刻で複雑なバグに対して自身を公開する可能性があります。マルチスレッドを使用しているソリューションを実装する前に、「マネージ スレッド処理の実施」を参照してください。
参照
処理手順
方法 : イベントベースの非同期パターンをサポートするコンポーネントを使用する
チュートリアル : イベントベースの非同期パターンをサポートするコンポーネントの実装
概念
イベントベースの非同期パターンを実装するための推奨される手順