Note

Access to this page requires authorization. You can try signing in or .

Access to this page requires authorization. You can try .

How to: Implement a Component That Supports the Event-based Asynchronous Pattern

The following code example implements a component with an asynchronous method, according to the Event-based Asynchronous Pattern Overview. The component is a prime number calculator that uses the Sieve of Eratosthenes algorithm to determine if a number is prime or composite.

There is extensive support for this task in Visual Studio. For more information, see Walkthrough: Implementing a Component That Supports the Event-based Asynchronous Pattern and Walkthrough: Implementing a Component That Supports the Event-based Asynchronous Pattern and Walkthrough: Implementing a Component That Supports the Event-based Asynchronous Pattern and Walkthrough: Implementing a Component That Supports the Event-based Asynchronous Pattern.

For an example client that uses the PrimeNumberCalculator component, see How to: Implement a Client of the Event-based Asynchronous Pattern.

Example

Imports System
Imports System.Collections
Imports System.Collections.Specialized
Imports System.ComponentModel
Imports System.Drawing
Imports System.Globalization
Imports System.Threading
Imports System.Windows.Forms


...



Public Delegate Sub ProgressChangedEventHandler( _
 ByVal e As ProgressChangedEventArgs)

Public Delegate Sub CalculatePrimeCompletedEventHandler( _
 ByVal sender As Object, _
 ByVal e As CalculatePrimeCompletedEventArgs)

' This class implements the Event-based Asynchronous Pattern.
' It asynchronously computes whether a number is prime or
' composite (not prime).
Public Class PrimeNumberCalculator
 Inherits System.ComponentModel.Component

 Private Delegate Sub WorkerEventHandler( _
 ByVal numberToCheck As Integer, _
 ByVal asyncOp As AsyncOperation)

 Private onProgressReportDelegate As SendOrPostCallback
 Private onCompletedDelegate As SendOrPostCallback

 Private userStateToLifetime As New HybridDictionary()

 Private components As System.ComponentModel.Container = Nothing

 '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
#Region "Public events"

 Public Event ProgressChanged _
 As ProgressChangedEventHandler
 Public Event CalculatePrimeCompleted _
 As CalculatePrimeCompletedEventHandler

#End Region

 '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
#Region "Construction and destruction"


 Public Sub New(ByVal container As System.ComponentModel.IContainer)

 container.Add(Me)
 InitializeComponent()

 InitializeDelegates()

 End Sub


 Public Sub New()

 InitializeComponent()

 InitializeDelegates()

 End Sub


 Protected Overridable Sub InitializeDelegates()
 onProgressReportDelegate = _
 New SendOrPostCallback(AddressOf ReportProgress)
 onCompletedDelegate = _
 New SendOrPostCallback(AddressOf CalculateCompleted)
 End Sub


 Protected Overrides Sub Dispose(ByVal disposing As Boolean)
 If disposing Then
 If (components IsNot Nothing) Then
 components.Dispose()
 End If
 End If
 MyBase.Dispose(disposing)

 End Sub

#End Region

 '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
#Region "Implementation"


 ' 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

 ' 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

 ' 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

 ' 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

 ' 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


 ' 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


 ' 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


 ' 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

#End Region


 Private Sub InitializeComponent()

 End Sub


End Class

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

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
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;


...



 /////////////////////////////////////////////////////////////
 #region PrimeNumberCalculator Implementation

 public delegate void ProgressChangedEventHandler(
 ProgressChangedEventArgs e);

 public delegate void CalculatePrimeCompletedEventHandler(
 object sender,
 CalculatePrimeCompletedEventArgs e);

 // This class implements the Event-based Asynchronous Pattern.
 // It asynchronously computes whether a number is prime or
 // composite (not prime).
 public class PrimeNumberCalculator : Component
 {
 private delegate void WorkerEventHandler(
 int numberToCheck,
 AsyncOperation asyncOp);

 private SendOrPostCallback onProgressReportDelegate;
 private SendOrPostCallback onCompletedDelegate;

 private HybridDictionary userStateToLifetime = 
 new HybridDictionary();

 private System.ComponentModel.Container components = null;

 /////////////////////////////////////////////////////////////
 #region Public events

 public event ProgressChangedEventHandler ProgressChanged;
 public event CalculatePrimeCompletedEventHandler CalculatePrimeCompleted;

 #endregion

 /////////////////////////////////////////////////////////////
 #region Construction and destruction

 public PrimeNumberCalculator(IContainer container)
 { 
 container.Add(this);
 InitializeComponent();

 InitializeDelegates();
 }

 public PrimeNumberCalculator()
 { 
 InitializeComponent();

 InitializeDelegates();
 }

 protected virtual void InitializeDelegates()
 {
 onProgressReportDelegate =
 new SendOrPostCallback(ReportProgress);
 onCompletedDelegate =
 new SendOrPostCallback(CalculateCompleted);
 }

 protected override void Dispose(bool disposing)
 {
 if (disposing)
 {
 if (components != null)
 {
 components.Dispose();
 }
 }
 base.Dispose(disposing);
 }

 #endregion // Construction and destruction

 /////////////////////////////////////////////////////////////
 ///
 #region Implementation

 // 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);
 }

 // Utility method for determining if a 
 // task has been canceled.
 private bool TaskCanceled(object taskId)
 {
 return( userStateToLifetime[taskId] == null );
 }

 // 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 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 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 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 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 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.
 }


 #endregion

 /////////////////////////////////////////////////////////////
 #region Component Designer generated code

 private void InitializeComponent()
 {
 components = new System.ComponentModel.Container();
 }

 #endregion

 }

 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 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;
 }
 }
 }


 #endregion

See Also

Tasks

Walkthrough: Implementing a Component That Supports the Event-based Asynchronous Pattern

Reference

AsyncOperation

AsyncOperationManager

WindowsFormsSynchronizationContext

Additional resources