方法: イベントベースの非同期パターンをサポートするコンポーネントを実装するHow to: Implement a Component That Supports the Event-based Asynchronous Pattern

顕著な遅延が発生する可能性がある操作を伴うクラスを作成する場合は、イベント ベースの非同期パターンを実装することによって、非同期機能を与えることを検討します。If you are writing a class with some operations that may incur noticeable delays, consider giving it asynchronous functionality by implementing the Event-based Asynchronous Pattern Overview.

このチュートリアルでは、イベント ベースの非同期パターンを実装するコンポーネントの作成方法を示します。This walkthrough illustrates how to create a component that implements the Event-based Asynchronous Pattern. これは、System.ComponentModel 名前空間のヘルパー クラスを使用して実装します。これにより、コンポーネントは任意のアプリケーション モデルで正常に動作します (ASP.NET、コンソール アプリケーション、Windows フォーム アプリケーションなど)。It is implemented using helper classes from the System.ComponentModel namespace, which ensures that the component works correctly under any application model, including ASP.NET, Console applications and Windows Forms applications. また、このコンポーネントは、PropertyGrid コントロールや独自のカスタム デザイナーで設計可能です。This component is also designable with a PropertyGrid control and your own custom designers.

このチュートリアルを完了すると、素数を非同期に計算するアプリケーションが作成されます。When you are through, you will have an application that computes prime numbers asynchronously. アプリケーションには、メイン ユーザー インターフェイス (UI) スレッドと各素数の計算用のスレッドがあります。Your application will have a main user interface (UI) thread and a thread for each prime number calculation. 大きな数が素数かどうかを調べるにはかなりの時間がかかることがありますが、この遅延によってメイン UI スレッドが中断されることはなく、計算中もフォームは応答性を維持します。Although testing whether a large number is prime can take a noticeable amount of time, the main UI thread will not be interrupted by this delay, and the form will be responsive during the calculations. いくつでも好きなだけ計算を同時に実行し、保留中の計算を選択的に取り消すことができます。You will be able to run as many calculations as you like concurrently and selectively cancel pending calculations.

このチュートリアルでは、以下のタスクを行います。Tasks illustrated in this walkthrough include:

  • コンポーネントの作成Creating the Component

  • パブリックの非同期イベントとデリゲートの定義Defining Public Asynchronous Events and Delegates

  • プライベート デリゲートの定義Defining Private Delegates

  • パブリック イベントの実装Implementing Public Events

  • 完了メソッドの実装Implementing the Completion Method

  • ワーカー メソッドの実装Implementing the Worker Methods

  • 開始メソッドとキャンセル メソッドの実装Implementing Start and Cancel Methods

このトピックのコードを単一のリストとしてコピーするには、「方法:イベントベースの非同期パターンのクライアントを実装する」をご覧ください。To copy the code in this topic as a single listing, see How to: Implement a Client of the Event-based Asynchronous Pattern.

コンポーネントの作成Creating the Component

最初に、イベント ベースの非同期パターンを実装するコンポーネントを作成します。The first step is to create the component that will implement the Event-based Asynchronous Pattern.

コンポーネントを作成するにはTo create the component

  • Component を継承する PrimeNumberCalculator というクラスを作成します。Create a class called PrimeNumberCalculator that inherits from Component.

パブリックの非同期イベントとデリゲートの定義Defining Public Asynchronous Events and Delegates

コンポーネントは、イベントを使ってクライアントと通信します。Your component communicates to clients using events. MethodNameCompleted イベントは非同期タスクの完了をクライアントに通知し、MethodNameProgressChanged イベントは非同期タスクの進行状況をクライアントに通知します。The MethodNameCompleted event alerts clients to the completion of an asynchronous task, and the MethodNameProgressChanged event informs clients of the progress of an asynchronous task.

コンポーネントのクライアント用の非同期イベントを定義するには:To define asynchronous events for clients of your component:

  1. ファイルの先頭で System.Threading および System.Collections.Specialized 名前空間をインポートします。Import the System.Threading and System.Collections.Specialized namespaces at the top of your file.

    using System;
    using System.Collections;
    using System.Collections.Specialized;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Globalization;
    using System.Threading;
    using System.Windows.Forms;
    
    Imports System.Collections
    Imports System.Collections.Specialized
    Imports System.ComponentModel
    Imports System.Drawing
    Imports System.Globalization
    Imports System.Threading
    Imports System.Windows.Forms
    
  2. PrimeNumberCalculator クラスの定義の前で、進行状況イベントと完了イベントのデリゲートを宣言します。Before the PrimeNumberCalculator class definition, declare delegates for progress and completion events.

    public delegate void ProgressChangedEventHandler(
        ProgressChangedEventArgs e);
    
    public delegate void CalculatePrimeCompletedEventHandler(
        object sender,
        CalculatePrimeCompletedEventArgs e);
    
    Public Delegate Sub ProgressChangedEventHandler( _
        ByVal e As ProgressChangedEventArgs)
    
    Public Delegate Sub CalculatePrimeCompletedEventHandler( _
        ByVal sender As Object, _
        ByVal e As CalculatePrimeCompletedEventArgs)
    
  3. PrimeNumberCalculator クラスの定義で、進行状況と完了をクライアントに報告するイベントを宣言します。In the PrimeNumberCalculator class definition, declare events for reporting progress and completion to clients.

    public event ProgressChangedEventHandler ProgressChanged;
    public event CalculatePrimeCompletedEventHandler CalculatePrimeCompleted;
    
    Public Event ProgressChanged _
        As ProgressChangedEventHandler
    Public Event CalculatePrimeCompleted _
        As CalculatePrimeCompletedEventHandler
    
  4. PrimeNumberCalculator クラスの定義の後で、CalculatePrimeCompleted イベントに対するクライアントのイベント ハンドラーに各計算の結果を報告するための CalculatePrimeCompletedEventArgs クラスを派生します。After the PrimeNumberCalculator class definition, derive the CalculatePrimeCompletedEventArgs class for reporting the outcome of each calculation to the client's event handler for the CalculatePrimeCompleted.event. AsyncCompletedEventArgs プロパティに加えて、このクラスにより、クライアントはテストされた値、その値が素数かどうか、素数でない場合は最初の約数を知ることができます。In addition to the AsyncCompletedEventArgs properties, this class enables the client to determine what number was tested, whether it is prime, and what the first divisor is if it is not prime.

    public class CalculatePrimeCompletedEventArgs :
        AsyncCompletedEventArgs
    {
        private int numberToTestValue = 0;
        private int firstDivisorValue = 1;
        private bool isPrimeValue;
    
        public CalculatePrimeCompletedEventArgs(
            int numberToTest,
            int firstDivisor,
            bool isPrime,
            Exception e,
            bool canceled,
            object state) : base(e, canceled, state)
        {
            this.numberToTestValue = numberToTest;
            this.firstDivisorValue = firstDivisor;
            this.isPrimeValue = isPrime;
        }
    
        public int NumberToTest
        {
            get
            {
                // Raise an exception if the operation failed or 
                // was canceled.
                RaiseExceptionIfNecessary();
    
                // If the operation was successful, return the 
                // property value.
                return numberToTestValue;
            }
        }
    
        public int FirstDivisor
        {
            get
            {
                // Raise an exception if the operation failed or 
                // was canceled.
                RaiseExceptionIfNecessary();
    
                // If the operation was successful, return the 
                // property value.
                return firstDivisorValue;
            }
        }
    
        public bool IsPrime
        {
            get
            {
                // Raise an exception if the operation failed or 
                // was canceled.
                RaiseExceptionIfNecessary();
    
                // If the operation was successful, return the 
                // property value.
                return isPrimeValue;
            }
        }
    }
    
    
    Public Class CalculatePrimeCompletedEventArgs
        Inherits AsyncCompletedEventArgs
        Private numberToTestValue As Integer = 0
        Private firstDivisorValue As Integer = 1
        Private isPrimeValue As Boolean
    
    
        Public Sub New( _
        ByVal numberToTest As Integer, _
        ByVal firstDivisor As Integer, _
        ByVal isPrime As Boolean, _
        ByVal e As Exception, _
        ByVal canceled As Boolean, _
        ByVal state As Object)
    
            MyBase.New(e, canceled, state)
            Me.numberToTestValue = numberToTest
            Me.firstDivisorValue = firstDivisor
            Me.isPrimeValue = isPrime
    
        End Sub
    
    
        Public ReadOnly Property NumberToTest() As Integer
            Get
                ' Raise an exception if the operation failed 
                ' or was canceled.
                RaiseExceptionIfNecessary()
    
                ' If the operation was successful, return 
                ' the property value.
                Return numberToTestValue
            End Get
        End Property
    
    
        Public ReadOnly Property FirstDivisor() As Integer
            Get
                ' Raise an exception if the operation failed 
                ' or was canceled.
                RaiseExceptionIfNecessary()
    
                ' If the operation was successful, return 
                ' the property value.
                Return firstDivisorValue
            End Get
        End Property
    
    
        Public ReadOnly Property IsPrime() As Boolean
            Get
                ' Raise an exception if the operation failed 
                ' or was canceled.
                RaiseExceptionIfNecessary()
    
                ' If the operation was successful, return 
                ' the property value.
                Return isPrimeValue
            End Get
        End Property
    End Class
    

チェックポイントCheckpoint

この段階で、コンポーネントをビルドすることができます。At this point, you can build the component.

コンポーネントをテストするにはTo test your component

  • コンポーネントをコンパイルします。Compile the component.

    コンパイラの警告が 2 つ表示されます。You will receive two compiler warnings:

    warning CS0067: The event 'AsynchronousPatternExample.PrimeNumberCalculator.ProgressChanged' is never used  
    warning CS0067: The event 'AsynchronousPatternExample.PrimeNumberCalculator.CalculatePrimeCompleted' is never used  
    

    これらの警告は次のセクションでなくなります。These warnings will be cleared in the next section.

プライベート デリゲートの定義Defining Private Delegates

PrimeNumberCalculator コンポーネントの非同期の側面は、SendOrPostCallback と呼ばれる特別なデリゲートを使って内部的に実装されます。The asynchronous aspects of the PrimeNumberCalculator component are implemented internally with a special delegate known as a SendOrPostCallback. SendOrPostCallback は、ThreadPool スレッドで実行するコールバック メソッドを表します。A SendOrPostCallback represents a callback method that executes on a ThreadPool thread. コールバック メソッドは、Object 型のパラメーターを 1 つ受け取るシグネチャを持つ必要があります。つまり、ラッパー クラスでデリゲートに状態を渡す必要があります。The callback method must have a signature that takes a single parameter of type Object, which means you will need to pass state among delegates in a wrapper class. 詳細については、SendOrPostCallback を参照してください。For more information, see SendOrPostCallback.

コンポーネントの内部非同期動作を実装するには:To implement your component's internal asynchronous behavior:

  1. PrimeNumberCalculator クラスで SendOrPostCallback デリゲートを宣言して作成します。Declare and create the SendOrPostCallback delegates in the PrimeNumberCalculator class. InitializeDelegates という名前のユーティリティ メソッドで SendOrPostCallback オブジェクトを作成します。Create the SendOrPostCallback objects in a utility method called InitializeDelegates.

    2 つのデリゲートが必要です。1 つはクライアントに進行状況を報告するためのもので、もう 1 つはクライアントに完了を報告するためのものです。You will need two delegates: one for reporting progress to the client, and one for reporting completion to the client.

    private SendOrPostCallback onProgressReportDelegate;
    private SendOrPostCallback onCompletedDelegate;
    
    Private onProgressReportDelegate As SendOrPostCallback
    Private onCompletedDelegate As SendOrPostCallback
    
    protected virtual void InitializeDelegates()
    {
        onProgressReportDelegate =
            new SendOrPostCallback(ReportProgress);
        onCompletedDelegate =
            new SendOrPostCallback(CalculateCompleted);
    }
    
    Protected Overridable Sub InitializeDelegates()
        onProgressReportDelegate = _
            New SendOrPostCallback(AddressOf ReportProgress)
        onCompletedDelegate = _
            New SendOrPostCallback(AddressOf CalculateCompleted)
    End Sub
    
  2. コンポーネントのコンストラクターで InitializeDelegates メソッドを呼び出します。Call the InitializeDelegates method in your component's constructor.

    public PrimeNumberCalculator()
    {   
        InitializeComponent();
    
        InitializeDelegates();
    }
    
    Public Sub New()
    
        InitializeComponent()
    
        InitializeDelegates()
    
    End Sub
    
  3. 実際の処理を非同期に行う PrimeNumberCalculator クラスのデリゲートを宣言します。Declare a delegate in the PrimeNumberCalculator class that handles the actual work to be done asynchronously. このデリゲートは、数値が素数かどうかをテストするワーカー メソッドをラップします。This delegate wraps the worker method that tests whether a number is prime. デリゲートは AsyncOperation パラメーターを受け取ります。このパラメーターは、非同期操作の有効期間を追跡するために使われます。The delegate takes an AsyncOperation parameter, which will be used to track the lifetime of the asynchronous operation.

    private delegate void WorkerEventHandler(
        int numberToCheck,
        AsyncOperation asyncOp);
    
    Private Delegate Sub WorkerEventHandler( _
    ByVal numberToCheck As Integer, _
    ByVal asyncOp As AsyncOperation)
    
  4. 保留中の非同期操作の有効期間を管理するためのコレクションを作成します。Create a collection for managing lifetimes of pending asynchronous operations. クライアントは操作の実行と完了を追跡する手段が必要であり、この追跡は、クライアントが非同期メソッドを呼び出すときに渡す必要のある一意のトークン (タスク ID) を使って行われます。The client needs a way to track operations as they are executed and completed, and this tracking is done by requiring the client to pass a unique token, or task ID, when the client makes the call to the asynchronous method. PrimeNumberCalculator コンポーネントは、タスク ID を対応する呼び出しと関連付けることによって、各呼び出しの追跡を維持する必要があります。The PrimeNumberCalculator component must keep track of each call by associating the task ID with its corresponding invocation. クライアントが一意でないタスク ID を渡した場合、PrimeNumberCalculator コンポーネントは例外を生成する必要があります。If the client passes a task ID that is not unique, the PrimeNumberCalculator component must raise an exception.

    PrimeNumberCalculator コンポーネントは、HybridDictionary という特別なコレクション クラスを使ってタスク ID を追跡します。The PrimeNumberCalculator component keeps track of task ID by using a special collection class called a HybridDictionary. クラスの定義で、userTokenToLifetime という名前の HybridDictionary を作成します。In the class definition, create a HybridDictionary called userTokenToLifetime.

    private HybridDictionary userStateToLifetime = 
        new HybridDictionary();
    
    Private userStateToLifetime As New HybridDictionary()
    

パブリック イベントの実装Implementing Public Events

イベント ベースの非同期パターンを実装するコンポーネントは、イベントを使ってクライアントに通信します。Components that implement the Event-based Asynchronous Pattern communicate to clients using events. これらのイベントは、AsyncOperation クラスを使って適切なスレッドで呼び出されます。These events are invoked on the proper thread with the help of the AsyncOperation class.

コンポーネントのクライアントに対するイベントを生成するには:To raise events to your component's clients:

  1. クライアントに報告するためのパブリック イベントを実装します。Implement public events for reporting to clients. 進行状況報告用のイベントと、完了報告用のイベントが必要です。You will need an event for reporting progress and one for reporting completion.

    // This method is invoked via the AsyncOperation object,
    // so it is guaranteed to be executed on the correct thread.
    private void CalculateCompleted(object operationState)
    {
        CalculatePrimeCompletedEventArgs e =
            operationState as CalculatePrimeCompletedEventArgs;
    
        OnCalculatePrimeCompleted(e);
    }
    
    // This method is invoked via the AsyncOperation object,
    // so it is guaranteed to be executed on the correct thread.
    private void ReportProgress(object state)
    {
        ProgressChangedEventArgs e =
            state as ProgressChangedEventArgs;
    
        OnProgressChanged(e);
    }
    
    protected void OnCalculatePrimeCompleted(
        CalculatePrimeCompletedEventArgs e)
    {
        if (CalculatePrimeCompleted != null)
        {
            CalculatePrimeCompleted(this, e);
        }
    }
    
    protected void OnProgressChanged(ProgressChangedEventArgs e)
    {
        if (ProgressChanged != null)
        {
            ProgressChanged(e);
        }
    }
    
    ' This method is invoked via the AsyncOperation object,
    ' so it is guaranteed to be executed on the correct thread.
    Private Sub CalculateCompleted(ByVal operationState As Object)
        Dim e As CalculatePrimeCompletedEventArgs = operationState
    
        OnCalculatePrimeCompleted(e)
    
    End Sub
    
    
    ' This method is invoked via the AsyncOperation object,
    ' so it is guaranteed to be executed on the correct thread.
    Private Sub ReportProgress(ByVal state As Object)
        Dim e As ProgressChangedEventArgs = state
    
        OnProgressChanged(e)
    
    End Sub
    
    Protected Sub OnCalculatePrimeCompleted( _
        ByVal e As CalculatePrimeCompletedEventArgs)
    
        RaiseEvent CalculatePrimeCompleted(Me, e)
    
    End Sub
    
    
    Protected Sub OnProgressChanged( _
        ByVal e As ProgressChangedEventArgs)
    
        RaiseEvent ProgressChanged(e)
    
    End Sub
    

完了メソッドの実装Implementing the Completion Method

完了デリゲートは、非同期操作が正常完了、エラー、またはキャンセルで終了すると、基になっているフリー スレッドの非同期動作が呼び出すメソッドです。The completion delegate is the method that the underlying, free-threaded asynchronous behavior will invoke when the asynchronous operation ends by successful completion, error, or cancellation. この呼び出しは、任意のスレッドで発生します。This invocation happens on an arbitrary thread.

このメソッドでは、クライアントのタスク ID が一意のクライアント トークンの内部コレクションから削除されます。This method is where the client's task ID is removed from the internal collection of unique client tokens. また、このメソッドは、対応する AsyncOperationPostOperationCompleted メソッドを呼び出すことにより、特定の非同期操作の有効期間を終了させます。This method also ends the lifetime of a particular asynchronous operation by calling the PostOperationCompleted method on the corresponding AsyncOperation. この呼び出しでは、アプリケーション モデルの適切なスレッドで完了イベントが発生します。This call raises the completion event on the thread that is appropriate for the application model. PostOperationCompleted メソッドが呼び出された後、AsyncOperation のこのインスタンスは使えなくなり、それ以降にこれを使おうとするとすべて例外がスローされます。After the PostOperationCompleted method is called, this instance of AsyncOperation can no longer be used, and any subsequent attempts to use it will throw an exception.

CompletionMethod のシグネチャは、非同期操作の結果を記述するために必要なすべての状態を保持する必要があります。The CompletionMethod signature must hold all state necessary to describe the outcome of the asynchronous operation. この特定の非同期操作によってテストされた値の状態、その値が素数かどうか、素数の場合は最初の約数を保持しています。It holds state for the number that was tested by this particular asynchronous operation, whether the number is prime, and the value of its first divisor if it is not a prime number. また、発生した例外を記述する状態、およびこの特定のタスクに対応する AsyncOperation も保持します。It also holds state describing any exception that occurred, and the AsyncOperation corresponding to this particular task.

非同期操作を完了するには:To complete an asynchronous operation:

  • 完了メソッドを実装します。Implement the completion method. このメソッドは 6 つのパラメーターを受け取り、それを使って、クライアントの CalculatePrimeCompletedEventHandler によってクライアントに返される CalculatePrimeCompletedEventArgs を設定します。It takes six parameters, which it uses to populate a CalculatePrimeCompletedEventArgs that is returned to the client through the client's CalculatePrimeCompletedEventHandler. また、クライアントのタスク ID トークンを内部コレクションから削除し、PostOperationCompleted を呼び出して非同期操作の有効期間を終了します。It removes the client's task ID token from the internal collection, and it ends the asynchronous operation's lifetime with a call to PostOperationCompleted. AsyncOperation は、アプリケーション モデルに適したスレッドまたはコンテキストへの呼び出しをマーシャリングします。The AsyncOperation marshals the call to the thread or context that is appropriate for the application model.

    // This is the method that the underlying, free-threaded 
    // asynchronous behavior will invoke.  This will happen on
    // an arbitrary thread.
    private void CompletionMethod( 
        int numberToTest,
        int firstDivisor, 
        bool isPrime,
        Exception exception, 
        bool canceled,
        AsyncOperation asyncOp )
    
    {
        // If the task was not previously canceled,
        // remove the task from the lifetime collection.
        if (!canceled)
        {
            lock (userStateToLifetime.SyncRoot)
            {
                userStateToLifetime.Remove(asyncOp.UserSuppliedState);
            }
        }
    
        // Package the results of the operation in a 
        // CalculatePrimeCompletedEventArgs.
        CalculatePrimeCompletedEventArgs e =
            new CalculatePrimeCompletedEventArgs(
            numberToTest,
            firstDivisor,
            isPrime,
            exception,
            canceled,
            asyncOp.UserSuppliedState);
    
        // End the task. The asyncOp object is responsible 
        // for marshaling the call.
        asyncOp.PostOperationCompleted(onCompletedDelegate, e);
    
        // Note that after the call to OperationCompleted, 
        // asyncOp is no longer usable, and any attempt to use it
        // will cause an exception to be thrown.
    }
    
    ' This is the method that the underlying, free-threaded 
    ' asynchronous behavior will invoke.  This will happen on
    '  an arbitrary thread.
    Private Sub CompletionMethod( _
        ByVal numberToTest As Integer, _
        ByVal firstDivisor As Integer, _
        ByVal prime As Boolean, _
        ByVal exc As Exception, _
        ByVal canceled As Boolean, _
        ByVal asyncOp As AsyncOperation)
    
        ' If the task was not previously canceled,
        ' remove the task from the lifetime collection.
        If Not canceled Then
            SyncLock userStateToLifetime.SyncRoot
                userStateToLifetime.Remove(asyncOp.UserSuppliedState)
            End SyncLock
        End If
    
        ' Package the results of the operation in a 
        ' CalculatePrimeCompletedEventArgs.
        Dim e As New CalculatePrimeCompletedEventArgs( _
            numberToTest, _
            firstDivisor, _
            prime, _
            exc, _
            canceled, _
            asyncOp.UserSuppliedState)
    
        ' End the task. The asyncOp object is responsible 
        ' for marshaling the call.
        asyncOp.PostOperationCompleted(onCompletedDelegate, e)
    
        ' Note that after the call to PostOperationCompleted, asyncOp
        ' is no longer usable, and any attempt to use it will cause.
        ' an exception to be thrown.
    
    End Sub
    

チェックポイントCheckpoint

この段階で、コンポーネントをビルドすることができます。At this point, you can build the component.

コンポーネントをテストするにはTo test your component

  • コンポーネントをコンパイルします。Compile the component.

    コンパイラの警告が 1 つ表示されます。You will receive one compiler warning:

    warning CS0169: The private field 'AsynchronousPatternExample.PrimeNumberCalculator.workerDelegate' is never used  
    

    この警告は次のセクションで解決されます。This warning will be resolved in the next section.

ワーカー メソッドの実装Implementing the Worker Methods

ここまでで、PrimeNumberCalculator コンポーネントをサポートする非同期コードを実装できました。So far, you have implemented the supporting asynchronous code for the PrimeNumberCalculator component. 次に、実際の処理を行うコードを実装できます。Now you can implement the code that does the actual work. 3 つのメソッド CalculateWorkerBuildPrimeNumberListIsPrime を実装します。You will implement three methods: CalculateWorker, BuildPrimeNumberList, and IsPrime. また、BuildPrimeNumberListIsPrime は、Sieve of Eratosthenes と呼ばれるよく知られたアルゴリズムを構成します。これは、テストする数値の平方根までのすべての素数を調べることで、その数値が素数かどうかを判定します。Together, BuildPrimeNumberList and IsPrime comprise a well-known algorithm called the Sieve of Eratosthenes, which determines if a number is prime by finding all the prime numbers up to the square root of the test number. 平方根までに約数が見つからない場合、テスト対象の値は素数です。If no divisors are found by that point, the test number is prime.

このコンポーネントが最大限の効率性で作成されているとしたら、異なるテスト数値のさまざまな呼び出しによって検出されたすべての素数を記憶しています。If this component were written for maximum efficiency, it would remember all the prime numbers discovered by various invocations for different test numbers. また、2、3、5 などの自明の約数もチェックします。It would also check for trivial divisors like 2, 3, and 5. ただし、この例の目的は時間のかかる操作を非同期に実行する方法を示すことなので、これらの最適化は自習のために残しておきます。The intent of this example is to demonstrate how time-consuming operations can be executed asynchronously, however, so these optimizations are left as an exercise for you.

CalculateWorker メソッドはデリゲートにラップされており、BeginInvoke の呼び出しとは非同期に呼び出されます。The CalculateWorker method is wrapped in a delegate and is invoked asynchronously with a call to BeginInvoke.

注意

進行状況の報告は、BuildPrimeNumberList メソッドで実装されています。Progress reporting is implemented in the BuildPrimeNumberList method. 高速なコンピューターでは、ProgressChanged イベントが立て続けに発生する可能性があります。On fast computers, ProgressChanged events can be raised in rapid succession. これらのイベントが生成されるクライアント スレッドは、このような状況を処理できる必要があります。The client thread, on which these events are raised, must be able to handle this situation. ユーザー インターフェイスのコードがメッセージであふれ、処理が追いつかなくなり、応答しなくなる可能性があります。User interface code may be flooded with messages and unable to keep up, resulting in unresponsiveness. この状況を処理するユーザー インターフェイスの例については、「方法:イベントベースの非同期パターンのクライアントを実装する」をご覧ください。For an example user interface that handles this situation, see How to: Implement a Client of the Event-based Asynchronous Pattern.

素数の計算を非同期に実行するには:To execute the prime number calculation asynchronously:

  1. TaskCanceled ユーティリティ メソッドを実装します。Implement the TaskCanceled utility method. このメソッドは、タスク有効期間コレクションで指定されたタスク ID をチェックし、タスク ID が見つからない場合は true を返します。This checks the task lifetime collection for the given task ID, and returns true if the task ID is not found.

    // Utility method for determining if a 
    // task has been canceled.
    private bool TaskCanceled(object taskId)
    {
        return( userStateToLifetime[taskId] == null );
    }
    
    ' Utility method for determining if a 
    ' task has been canceled.
    Private Function TaskCanceled(ByVal taskId As Object) As Boolean
        Return (userStateToLifetime(taskId) Is Nothing)
    End Function
    
  2. CalculateWorker メソッドを実装します。Implement the CalculateWorker method. このメソッドは、テストする数と AsyncOperation の 2 つのパラメーターを受け取ります。It takes two parameters: a number to test, and an AsyncOperation.

    // This method performs the actual prime number computation.
    // It is executed on the worker thread.
    private void CalculateWorker(
        int numberToTest,
        AsyncOperation asyncOp)
    {
        bool isPrime = false;
        int firstDivisor = 1;
        Exception e = null;
    
        // Check that the task is still active.
        // The operation may have been canceled before
        // the thread was scheduled.
        if (!TaskCanceled(asyncOp.UserSuppliedState))
        {
            try
            {
                // Find all the prime numbers up to 
                // the square root of numberToTest.
                ArrayList primes = BuildPrimeNumberList(
                    numberToTest,
                    asyncOp);
    
                // Now we have a list of primes less than
                // numberToTest.
                isPrime = IsPrime(
                    primes,
                    numberToTest,
                    out firstDivisor);
            }
            catch (Exception ex)
            {
                e = ex;
            }
        }
    
        //CalculatePrimeState calcState = new CalculatePrimeState(
        //        numberToTest,
        //        firstDivisor,
        //        isPrime,
        //        e,
        //        TaskCanceled(asyncOp.UserSuppliedState),
        //        asyncOp);
    
        //this.CompletionMethod(calcState);
    
        this.CompletionMethod(
            numberToTest,
            firstDivisor,
            isPrime,
            e,
            TaskCanceled(asyncOp.UserSuppliedState),
            asyncOp);
    
        //completionMethodDelegate(calcState);
    }
    
    ' This method performs the actual prime number computation.
    ' It is executed on the worker thread.
    Private Sub CalculateWorker( _
        ByVal numberToTest As Integer, _
        ByVal asyncOp As AsyncOperation)
    
        Dim prime As Boolean = False
        Dim firstDivisor As Integer = 1
        Dim exc As Exception = Nothing
    
        ' Check that the task is still active.
        ' The operation may have been canceled before
        ' the thread was scheduled.
        If Not Me.TaskCanceled(asyncOp.UserSuppliedState) Then
    
            Try
                ' Find all the prime numbers up to the
                ' square root of numberToTest.
                Dim primes As ArrayList = BuildPrimeNumberList( _
                    numberToTest, asyncOp)
    
                ' Now we have a list of primes less than 
                'numberToTest.
                prime = IsPrime( _
                    primes, _
                    numberToTest, _
                    firstDivisor)
    
            Catch ex As Exception
                exc = ex
            End Try
    
        End If
    
        Me.CompletionMethod( _
            numberToTest, _
            firstDivisor, _
            prime, _
            exc, _
            TaskCanceled(asyncOp.UserSuppliedState), _
            asyncOp)
    
    End Sub
    
  3. BuildPrimeNumberListを実装します。Implement BuildPrimeNumberList. このメソッドは、テストする数と AsyncOperation の 2 つのパラメーターを受け取ります。It takes two parameters: the number to test, and an AsyncOperation. AsyncOperation を使って、進行状況とインクリメンタルな結果を報告します。It uses the AsyncOperation to report progress and incremental results. これにより、アプリケーション モデルの適切なスレッドまたはコンテキストで、クライアントのイベント ハンドラーが呼び出されます。This assures that the client's event handlers are called on the proper thread or context for the application model. BuildPrimeNumberList は、素数を見つけると、ProgressChanged イベントに対するクライアントのイベント ハンドラーに、インクリメンタルな結果としてこれを報告します。When BuildPrimeNumberList finds a prime number, it reports this as an incremental result to the client's event handler for the ProgressChanged event. これには ProgressChangedEventArgs から派生した CalculatePrimeProgressChangedEventArgs という名前のクラスが必要であり、このクラスには LatestPrimeNumber という名前の追加プロパティが 1 つあります。This requires a class derived from ProgressChangedEventArgs, called CalculatePrimeProgressChangedEventArgs, which has one added property called LatestPrimeNumber.

    BuildPrimeNumberList メソッドは TaskCanceled メソッドも定期的に呼び出し、このメソッドが true を返すと終了します。The BuildPrimeNumberList method also periodically calls the TaskCanceled method and exits if the method returns true.

    // This method computes the list of prime numbers used by the
    // IsPrime method.
    private ArrayList BuildPrimeNumberList(
        int numberToTest,
        AsyncOperation asyncOp)
    {
        ProgressChangedEventArgs e = null;
        ArrayList primes = new ArrayList();
        int firstDivisor;
        int n = 5;
    
        // Add the first prime numbers.
        primes.Add(2);
        primes.Add(3);
    
        // Do the work.
        while (n < numberToTest && 
               !TaskCanceled( asyncOp.UserSuppliedState ) )
        {
            if (IsPrime(primes, n, out firstDivisor))
            {
                // Report to the client that a prime was found.
                e = new CalculatePrimeProgressChangedEventArgs(
                    n,
                    (int)((float)n / (float)numberToTest * 100),
                    asyncOp.UserSuppliedState);
    
                asyncOp.Post(this.onProgressReportDelegate, e);
    
                primes.Add(n);
    
                // Yield the rest of this time slice.
                Thread.Sleep(0);
            }
    
            // Skip even numbers.
            n += 2;
        }
    
        return primes;
    }
    
    ' This method computes the list of prime numbers used by the
    ' IsPrime method.
    Private Function BuildPrimeNumberList( _
        ByVal numberToTest As Integer, _
        ByVal asyncOp As AsyncOperation) As ArrayList
    
        Dim e As ProgressChangedEventArgs = Nothing
        Dim primes As New ArrayList
        Dim firstDivisor As Integer
        Dim n As Integer = 5
    
        ' Add the first prime numbers.
        primes.Add(2)
        primes.Add(3)
    
        ' Do the work.
        While n < numberToTest And _
            Not Me.TaskCanceled(asyncOp.UserSuppliedState)
    
            If IsPrime(primes, n, firstDivisor) Then
                ' Report to the client that you found a prime.
                e = New CalculatePrimeProgressChangedEventArgs( _
                    n, _
                    CSng(n) / CSng(numberToTest) * 100, _
                    asyncOp.UserSuppliedState)
    
                asyncOp.Post(Me.onProgressReportDelegate, e)
    
                primes.Add(n)
    
                ' Yield the rest of this time slice.
                Thread.Sleep(0)
            End If
    
            ' Skip even numbers.
            n += 2
    
        End While
    
        Return primes
    
    End Function
    
  4. IsPrimeを実装します。Implement IsPrime. このメソッドは、既知の素数のリスト、テスト対象の数、および見つかった最初の約数の出力パラメーターの、3 つのパラメーターを受け取ります。It takes three parameters: a list of known prime numbers, the number to test, and an output parameter for the first divisor found. 素数のリストを基に、テスト対象の数が素数かどうかを特定します。Given the list of prime numbers, it determines if the test number is prime.

    // This method tests n for primality against the list of 
    // prime numbers contained in the primes parameter.
    private bool IsPrime(
        ArrayList primes,
        int n,
        out int firstDivisor)
    {
        bool foundDivisor = false;
        bool exceedsSquareRoot = false;
    
        int i = 0;
        int divisor = 0;
        firstDivisor = 1;
    
        // Stop the search if:
        // there are no more primes in the list,
        // there is a divisor of n in the list, or
        // there is a prime that is larger than 
        // the square root of n.
        while (
            (i < primes.Count) &&
            !foundDivisor &&
            !exceedsSquareRoot)
        {
            // The divisor variable will be the smallest 
            // prime number not yet tried.
            divisor = (int)primes[i++];
    
            // Determine whether the divisor is greater
            // than the square root of n.
            if (divisor * divisor > n)
            {
                exceedsSquareRoot = true;
            }
            // Determine whether the divisor is a factor of n.
            else if (n % divisor == 0)
            {
                firstDivisor = divisor;
                foundDivisor = true;
            }
        }
    
        return !foundDivisor;
    }
    
    ' This method tests n for primality against the list of 
    ' prime numbers contained in the primes parameter.
    Private Function IsPrime( _
        ByVal primes As ArrayList, _
        ByVal n As Integer, _
        ByRef firstDivisor As Integer) As Boolean
    
        Dim foundDivisor As Boolean = False
        Dim exceedsSquareRoot As Boolean = False
    
        Dim i As Integer = 0
        Dim divisor As Integer = 0
        firstDivisor = 1
    
        ' Stop the search if:
        ' there are no more primes in the list,
        ' there is a divisor of n in the list, or
        ' there is a prime that is larger than 
        ' the square root of n.
        While i < primes.Count AndAlso _
            Not foundDivisor AndAlso _
            Not exceedsSquareRoot
    
            ' The divisor variable will be the smallest prime number 
            ' not yet tried.
            divisor = primes(i)
            i = i + 1
    
            ' Determine whether the divisor is greater than the 
            ' square root of n.
            If divisor * divisor > n Then
                exceedsSquareRoot = True
                ' Determine whether the divisor is a factor of n.
            ElseIf n Mod divisor = 0 Then
                firstDivisor = divisor
                foundDivisor = True
            End If
        End While
    
        Return Not foundDivisor
    
    End Function
    
  5. ProgressChangedEventArgs から CalculatePrimeProgressChangedEventArgs を派生します。Derive CalculatePrimeProgressChangedEventArgs from ProgressChangedEventArgs. このクラスは、ProgressChanged イベントに対するクライアントのイベント ハンドラーにインクリメンタルな結果を報告するために必要です。This class is necessary for reporting incremental results to the client's event handler for the ProgressChanged event. LatestPrimeNumber という名前の追加プロパティが 1 つあります。It has one added property called LatestPrimeNumber.

    public class CalculatePrimeProgressChangedEventArgs :
            ProgressChangedEventArgs
    {
        private int latestPrimeNumberValue = 1;
    
        public CalculatePrimeProgressChangedEventArgs(
            int latestPrime,
            int progressPercentage,
            object userToken) : base( progressPercentage, userToken )
        {
            this.latestPrimeNumberValue = latestPrime;
        }
    
        public int LatestPrimeNumber
        {
            get
            {
                return latestPrimeNumberValue;
            }
        }
    }
    
    Public Class CalculatePrimeProgressChangedEventArgs
        Inherits ProgressChangedEventArgs
        Private latestPrimeNumberValue As Integer = 1
    
    
        Public Sub New( _
            ByVal latestPrime As Integer, _
            ByVal progressPercentage As Integer, _
            ByVal UserState As Object)
    
            MyBase.New(progressPercentage, UserState)
            Me.latestPrimeNumberValue = latestPrime
    
        End Sub
    
        Public ReadOnly Property LatestPrimeNumber() As Integer
            Get
                Return latestPrimeNumberValue
            End Get
        End Property
    End Class
    

チェックポイントCheckpoint

この段階で、コンポーネントをビルドすることができます。At this point, you can build the component.

コンポーネントをテストするにはTo test your component

  • コンポーネントをコンパイルします。Compile the component.

    残っているのは、非同期操作を開始およびキャンセルするメソッドである CalculatePrimeAsyncCancelAsync です。All that remains to be written are the methods to start and cancel asynchronous operations, CalculatePrimeAsync and CancelAsync.

開始メソッドとキャンセル メソッドの実装Implementing the Start and Cancel Methods

ラップするデリゲートで BeginInvoke を呼び出すことにより、専用のスレッドでワーカー メソッドを開始します。You start the worker method on its own thread by calling BeginInvoke on the delegate that wraps it. 特定の非同期操作の有効期間を管理するには、AsyncOperationManager ヘルパー クラスの CreateOperation メソッドを呼び出します。To manage the lifetime of a particular asynchronous operation, you call the CreateOperation method on the AsyncOperationManager helper class. このメソッドが返す AsyncOperation は、クライアントのイベント ハンドラーに対する呼び出しを適切なスレッドまたはコンテキストにマーシャリングします。This returns an AsyncOperation, which marshals calls on the client's event handlers to the proper thread or context.

特定の保留中操作を取り消すには、対応する AsyncOperationPostOperationCompleted を呼び出します。You cancel a particular pending operation by calling PostOperationCompleted on its corresponding AsyncOperation. このメソッドはその操作を終了するので、それ以降に AsyncOperation を呼び出すと例外がスローされます。This ends that operation, and any subsequent calls to its AsyncOperation will throw an exception.

開始とキャンセルの機能を実装するには:To implement Start and Cancel functionality:

  1. CalculatePrimeAsync メソッドを実装します。Implement the CalculatePrimeAsync method. クライアントが提供したトークン (タスク ID) が、現在保留中のタスクを表すすべてのトークンの間で一意であることを確認します。Make sure the client-supplied token (task ID) is unique with respect to all the tokens representing currently pending tasks. クライアントが一意ではないトークンを渡した場合、CalculatePrimeAsync は例外を生成します。If the client passes in a non-unique token, CalculatePrimeAsync raises an exception. 一意の場合は、トークンはタスク ID のコレクションに追加されます。Otherwise, the token is added to the task ID collection.

    // This method starts an asynchronous calculation. 
    // First, it checks the supplied task ID for uniqueness.
    // If taskId is unique, it creates a new WorkerEventHandler 
    // and calls its BeginInvoke method to start the calculation.
    public virtual void CalculatePrimeAsync(
        int numberToTest,
        object taskId)
    {
        // Create an AsyncOperation for taskId.
        AsyncOperation asyncOp =
            AsyncOperationManager.CreateOperation(taskId);
    
        // Multiple threads will access the task dictionary,
        // so it must be locked to serialize access.
        lock (userStateToLifetime.SyncRoot)
        {
            if (userStateToLifetime.Contains(taskId))
            {
                throw new ArgumentException(
                    "Task ID parameter must be unique", 
                    "taskId");
            }
    
            userStateToLifetime[taskId] = asyncOp;
        }
    
        // Start the asynchronous operation.
        WorkerEventHandler workerDelegate = new WorkerEventHandler(CalculateWorker);
        workerDelegate.BeginInvoke(
            numberToTest,
            asyncOp,
            null,
            null);
    }
    
    ' This method starts an asynchronous calculation. 
    ' First, it checks the supplied task ID for uniqueness.
    ' If taskId is unique, it creates a new WorkerEventHandler 
    ' and calls its BeginInvoke method to start the calculation.
    Public Overridable Sub CalculatePrimeAsync( _
        ByVal numberToTest As Integer, _
        ByVal taskId As Object)
    
        ' Create an AsyncOperation for taskId.
        Dim asyncOp As AsyncOperation = _
            AsyncOperationManager.CreateOperation(taskId)
    
        ' Multiple threads will access the task dictionary,
        ' so it must be locked to serialize access.
        SyncLock userStateToLifetime.SyncRoot
            If userStateToLifetime.Contains(taskId) Then
                Throw New ArgumentException( _
                    "Task ID parameter must be unique", _
                    "taskId")
            End If
    
            userStateToLifetime(taskId) = asyncOp
        End SyncLock
    
        ' Start the asynchronous operation.
        Dim workerDelegate As New WorkerEventHandler( _
            AddressOf CalculateWorker)
    
        workerDelegate.BeginInvoke( _
            numberToTest, _
            asyncOp, _
            Nothing, _
            Nothing)
    
    End Sub
    
  2. CancelAsync メソッドを実装します。Implement the CancelAsync method. トークン コレクションに taskId パラメーターが存在する場合は、削除されます。If the taskId parameter exists in the token collection, it is removed. これにより、開始する前に取り消されたタスクが実行するのを防ぎます。This prevents canceled tasks that have not started from running. タスクが実行中の場合、BuildPrimeNumberList メソッドは、タスク ID が有効期間コレクションから削除されたことを検出すると終了します。If the task is running, the BuildPrimeNumberList method exits when it detects that the task ID has been removed from the lifetime collection.

    // This method cancels a pending asynchronous operation.
    public void CancelAsync(object taskId)
    {
        AsyncOperation asyncOp = userStateToLifetime[taskId] as AsyncOperation;
        if (asyncOp != null)
        {   
            lock (userStateToLifetime.SyncRoot)
            {
                userStateToLifetime.Remove(taskId);
            }
        }
    }
    
    ' This method cancels a pending asynchronous operation.
    Public Sub CancelAsync(ByVal taskId As Object)
    
        Dim obj As Object = userStateToLifetime(taskId)
        If (obj IsNot Nothing) Then
    
            SyncLock userStateToLifetime.SyncRoot
    
                userStateToLifetime.Remove(taskId)
    
            End SyncLock
    
        End If
    
    End Sub
    

チェックポイントCheckpoint

この段階で、コンポーネントをビルドすることができます。At this point, you can build the component.

コンポーネントをテストするにはTo test your component

  • コンポーネントをコンパイルします。Compile the component.

PrimeNumberCalculator コンポーネントが完全して使用できるようになります。The PrimeNumberCalculator component is now complete and ready to use.

PrimeNumberCalculator コンポーネントを使うクライアントの例については、「方法:イベントベースの非同期パターンのクライアントを実装する」をご覧ください。For an example client that uses the PrimeNumberCalculator component, see How to: Implement a Client of the Event-based Asynchronous Pattern.

次の手順Next Steps

CalculatePrimeAsync メソッドに相当する同期メソッドである CalculatePrime を作成して、この例を拡張できます。You can fill out this example by writing CalculatePrime, the synchronous equivalent of CalculatePrimeAsync method. このようにすると、PrimeNumberCalculator コンポーネントはイベント ベースの非同期パターンに完全に準拠するようになります。This will make the PrimeNumberCalculator component fully compliant with the Event-based Asynchronous Pattern.

異なるテスト対象の数に対するさまざまな呼び出しによって検出されたすべての素数のリストを保持することで、この例を改良できます。You can improve this example by retaining the list of all the prime numbers discovered by various invocations for different test numbers. この方法を使うと、各タスクはそれより前のタスクで行われた作業の結果を流用できます。Using this approach, each task will benefit from the work done by previous tasks. 異なるスレッドによるリストへのアクセスがシリアル化されるように、このリストを lock 領域で保護することに注意してください。Be careful to protect this list with lock regions, so access to the list by different threads is serialized.

2、3、5 などの自明の約数をテストすることにより、この例を改良することもできます。You can also improve this example by testing for trivial divisors, like 2, 3, and 5.

関連項目See also