Delen via


Procedure: Een onderdeel implementeren dat het op gebeurtenissen gebaseerde asynchrone patroon ondersteunt

Als u een klasse schrijft met enkele bewerkingen die merkbare vertragingen kunnen oplopen, kunt u overwegen deze asynchrone functionaliteit te geven door het op gebeurtenissen gebaseerde Asynchrone patroonoverzicht te implementeren.

In dit scenario ziet u hoe u een onderdeel maakt waarmee het op gebeurtenissen gebaseerde asynchrone patroon wordt geïmplementeerd. Het wordt geïmplementeerd met behulp van helperklassen uit de System.ComponentModel naamruimte, die ervoor zorgt dat het onderdeel correct werkt onder elk toepassingsmodel, waaronder ASP.NET, consoletoepassingen en Windows Forms toepassingen. Dit onderdeel is ook ontwerpbaar met een PropertyGrid besturingselement en uw eigen aangepaste ontwerpers.

Wanneer u klaar bent, hebt u een toepassing waarmee priemgetallen asynchroon worden berekend. Uw toepassing heeft een ui-thread (hoofdgebruikersinterface) en een thread voor elke berekening van priemgetallen. Hoewel het testen of een groot getal priem is een merkbare hoeveelheid tijd kan duren, wordt de hoofd-UI-thread niet onderbroken door deze vertraging en reageert het formulier tijdens de berekeningen. U kunt zo veel berekeningen tegelijk uitvoeren als u wilt en in behandeling zijnde berekeningen selectief annuleren.

Taken die in dit scenario worden geïllustreerd, zijn onder andere:

  • Het onderdeel maken

  • Openbare asynchrone gebeurtenissen en gemachtigden definiëren

  • Persoonlijke gemachtigden definiëren

  • Openbare gebeurtenissen implementeren

  • De voltooiingsmethode implementeren

  • De werkmethoden implementeren

  • De begin- en annuleringsmethoden implementeren

Als u de code in dit onderwerp als één vermelding wilt kopiëren, raadpleegt u How to: Implement a client of the Event-based Asynchronous Pattern (Een client implementeren met het op gebeurtenissen gebaseerde asynchrone patroon).

Het onderdeel maken

De eerste stap is het maken van het onderdeel waarmee het op gebeurtenissen gebaseerde asynchrone patroon wordt geïmplementeerd.

Het onderdeel maken

  • Maak een klasse met de naam PrimeNumberCalculator die wordt overgenomen van Component.

Openbare asynchrone gebeurtenissen en gemachtigden definiëren

Uw onderdeel communiceert met clients met behulp van gebeurtenissen. De gebeurtenis MethodNameCompleted waarschuwt clients voor het voltooien van een asynchrone taak en de gebeurtenis MethodNameProgressChanged informeert clients over de voortgang van een asynchrone taak.

Asynchrone gebeurtenissen definiëren voor clients van uw onderdeel:

  1. Importeer de System.Threading naamruimten en System.Collections.Specialized boven aan het bestand.

    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. Vóór de PrimeNumberCalculator klassedefinitie declareert u gemachtigden voor voortgangs- en voltooiingsgebeurtenissen.

    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. Declareer in de PrimeNumberCalculator klassedefinitie gebeurtenissen voor het rapporteren van voortgang en voltooiing aan clients.

    public event ProgressChangedEventHandler ProgressChanged;
    public event CalculatePrimeCompletedEventHandler CalculatePrimeCompleted;
    
    Public Event ProgressChanged _
        As ProgressChangedEventHandler
    Public Event CalculatePrimeCompleted _
        As CalculatePrimeCompletedEventHandler
    
  4. Na de PrimeNumberCalculator klassedefinitie leidt u de CalculatePrimeCompletedEventArgs klasse af voor het rapporteren van het resultaat van elke berekening aan de gebeurtenis-handler van de client voor de CalculatePrimeCompleted.gebeurtenis. Naast de AsyncCompletedEventArgs eigenschappen kan de client met deze klasse bepalen welk getal is getest, of het priemgetal is en wat de eerste deler is als het geen priem is.

    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
    

Controlepunt 1

Op dit punt kunt u het onderdeel bouwen.

Uw onderdeel testen

  • Compileer het onderdeel.

    U ontvangt twee compilerwaarschuwingen:

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

    Deze waarschuwingen worden in de volgende sectie gewist.

Persoonlijke gemachtigden definiëren

De asynchrone aspecten van het PrimeNumberCalculator onderdeel worden intern geïmplementeerd met een speciale gemachtigde die een wordt genoemd SendOrPostCallback. A SendOrPostCallback vertegenwoordigt een callback-methode die wordt uitgevoerd op een ThreadPool thread. De callback-methode moet een handtekening hebben die één parameter van het type Objectgebruikt, wat betekent dat u de status moet doorgeven aan gedelegeerden in een wrapper-klasse. Voor meer informatie raadpleegt u SendOrPostCallback.

Het interne asynchrone gedrag van uw onderdeel implementeren:

  1. Declareer en maak de SendOrPostCallback gemachtigden in de PrimeNumberCalculator klasse. Maak de SendOrPostCallback objecten in een hulpprogramma met de naam InitializeDelegates.

    U hebt twee gemachtigden nodig: één voor het rapporteren van de voortgang aan de client en één voor het rapporteren van voltooiing aan de 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. Roep de methode aan in de InitializeDelegates constructor van het onderdeel.

    public PrimeNumberCalculator()
    {
        InitializeComponent();
    
        InitializeDelegates();
    }
    
    Public Sub New()
    
        InitializeComponent()
    
        InitializeDelegates()
    
    End Sub
    
  3. Declareer een gemachtigde in de PrimeNumberCalculator klasse die het werkelijke werk afhandelt dat asynchroon moet worden uitgevoerd. Deze gemachtigde verpakt de werkmethode waarmee wordt getest of een getal priem is. De gemachtigde neemt een AsyncOperation parameter, die wordt gebruikt om de levensduur van de asynchrone bewerking bij te houden.

    private delegate void WorkerEventHandler(
        int numberToCheck,
        AsyncOperation asyncOp);
    
    Private Delegate Sub WorkerEventHandler( _
    ByVal numberToCheck As Integer, _
    ByVal asyncOp As AsyncOperation)
    
  4. Maak een verzameling voor het beheren van de levensduur van asynchrone bewerkingen die in behandeling zijn. De client heeft een manier nodig om bewerkingen bij te houden wanneer deze worden uitgevoerd en voltooid. Dit wordt bijgehouden door te vereisen dat de client een uniek token of taak-id doorgeeft wanneer de client de asynchrone methode aanroept. Het PrimeNumberCalculator onderdeel moet elke aanroep bijhouden door de taak-id te koppelen aan de bijbehorende aanroep. Als de client een taak-id doorgeeft die niet uniek is, moet het PrimeNumberCalculator onderdeel een uitzondering genereren.

    Het PrimeNumberCalculator onderdeel houdt de taak-id bij met behulp van een speciale verzamelingsklasse met de naam .HybridDictionary Maak in de klassedefinitie een HybridDictionary met de naam userStateToLifetime.

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

Openbare gebeurtenissen implementeren

Onderdelen die het op gebeurtenissen gebaseerde asynchrone patroon implementeren, communiceren met clients met behulp van gebeurtenissen. Deze gebeurtenissen worden aangeroepen op de juiste thread met behulp van de AsyncOperation klasse.

Gebeurtenissen verzenden naar de clients van uw onderdeel:

  1. Implementeer openbare gebeurtenissen voor rapportage aan clients. U hebt een gebeurtenis nodig voor het rapporteren van de voortgang en een voor het voltooien van de rapportage.

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

De voltooiingsmethode implementeren

De gemachtigde voor voltooiing is de methode die door het onderliggende asynchrone gedrag met vrije threads wordt aangeroepen wanneer de asynchrone bewerking wordt beëindigd na een geslaagde voltooiing, fout of annulering. Deze aanroep vindt plaats op een willekeurige thread.

Bij deze methode wordt de taak-id van de client verwijderd uit de interne verzameling unieke clienttokens. Deze methode beëindigt ook de levensduur van een bepaalde asynchrone bewerking door de PostOperationCompleted methode aan te roepen op de bijbehorende AsyncOperation. Met deze aanroep wordt de voltooiingsevenement gegenereerd voor de thread die geschikt is voor het toepassingsmodel. Nadat de PostOperationCompleted methode is aangeroepen, kan dit exemplaar van AsyncOperation niet meer worden gebruikt en eventuele volgende pogingen om deze te gebruiken, genereren een uitzondering.

De CompletionMethod handtekening moet alle statussen bevatten die nodig zijn om het resultaat van de asynchrone bewerking te beschrijven. Het bevat de status voor het getal dat is getest door deze specifieke asynchrone bewerking, of het getal priem is en de waarde van de eerste deler als het geen priemgetal is. Het bevat ook de status die elke uitzondering beschrijft die is opgetreden en de AsyncOperation die overeenkomt met deze specifieke taak.

Een asynchrone bewerking voltooien:

  • Implementeer de voltooiingsmethode. Er zijn zes parameters nodig, die worden gebruikt om een CalculatePrimeCompletedEventArgs te vullen die naar de client wordt geretourneerd via de CalculatePrimeCompletedEventHandler. Het taak-id-token van de client wordt verwijderd uit de interne verzameling en de levensduur van de asynchrone bewerking wordt beëindigd met een aanroep naar PostOperationCompleted. De AsyncOperation marshals de aanroep naar de thread of context die geschikt is voor het toepassingsmodel.

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

Controlepunt 2

Op dit punt kunt u het onderdeel bouwen.

Uw onderdeel testen

  • Compileer het onderdeel.

    U ontvangt één compilerwaarschuwing:

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

    Deze waarschuwing wordt opgelost in de volgende sectie.

De werkmethoden implementeren

Tot nu toe hebt u de ondersteunende asynchrone code voor het PrimeNumberCalculator onderdeel geïmplementeerd. U kunt nu de code implementeren waarmee het werkelijke werk wordt uitgevoerd. U implementeert drie methoden: CalculateWorker, BuildPrimeNumberListen IsPrime. Samen vormen BuildPrimeNumberList ze IsPrime een bekend algoritme genaamd de zeef van Eratosthenes, dat bepaalt of een getal priem is door alle priemgetallen tot aan de vierkantswortel van het testgetal te vinden. Als er op dat punt geen delers worden gevonden, is het testgetal priemgetal.

Als dit onderdeel is geschreven voor maximale efficiëntie, worden alle priemgetallen onthouden die door verschillende aanroepen voor verschillende testnummers worden gedetecteerd. Er wordt ook gecontroleerd op triviale delers zoals 2, 3 en 5. Het doel van dit voorbeeld is om te laten zien hoe tijdrovende bewerkingen asynchroon kunnen worden uitgevoerd, zodat deze optimalisaties als oefening voor u worden achtergelaten.

De CalculateWorker methode wordt verpakt in een gedelegeerde en wordt asynchroon aangeroepen met een aanroep van BeginInvoke.

Notitie

Voortgangsrapportage wordt geïmplementeerd in de BuildPrimeNumberList methode. Op snelle computers ProgressChanged kunnen gebeurtenissen snel achter elkaar worden gegenereerd. De clientthread waarop deze gebeurtenissen worden gegenereerd, moet deze situatie kunnen afhandelen. De code van de gebruikersinterface kan worden overspoeld met berichten en kan niet worden bijgehouden, waardoor er niet meer wordt gereageerd. Zie How to: Implement a client of the Event-based Asynchronous Pattern (Een client implementeren met het op gebeurtenissen gebaseerde asynchrone patroon) voor een voorbeeld van een gebruikersinterface die deze situatie afhandelt.

De berekening van priemgetallen asynchroon uitvoeren:

  1. Implementeer de TaskCanceled hulpprogrammamethode. Hiermee wordt de verzameling levensduur van de taak voor de opgegeven taak-id gecontroleerd en wordt geretourneerd true als de taak-id niet wordt gevonden.

    // 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. Implementeer de CalculateWorker methode. Er zijn twee parameters nodig: een getal om te testen en een 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. Implementeer BuildPrimeNumberList. Er zijn twee parameters nodig: het aantal dat moet worden getest en een AsyncOperation. De wordt gebruikt AsyncOperation om de voortgang en incrementele resultaten te rapporteren. Dit zorgt ervoor dat de gebeurtenis-handlers van de client worden aangeroepen op de juiste thread of context voor het toepassingsmodel. Wanneer BuildPrimeNumberList een priemgetall wordt gevonden, wordt dit als incrementeel resultaat gerapporteerd aan de gebeurtenis-handler van de client voor de ProgressChanged gebeurtenis. Hiervoor is een klasse vereist die is afgeleid van ProgressChangedEventArgs, met de naam CalculatePrimeProgressChangedEventArgs, die een toegevoegde eigenschap heeft met de naam LatestPrimeNumber.

    De BuildPrimeNumberList methode roept ook periodiek de TaskCanceled methode aan en wordt afgesloten als de methode retourneert 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. Implementeer IsPrime. Er zijn drie parameters nodig: een lijst met bekende priemgetallen, het getal dat moet worden getest en een uitvoerparameter voor de eerste gevonden deler. Op basis van de lijst met priemgetallen wordt bepaald of het testnummer priem is.

    // 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. Afgeleid CalculatePrimeProgressChangedEventArgs van ProgressChangedEventArgs. Deze klasse is nodig voor het rapporteren van incrementele resultaten aan de gebeurtenis-handler van de client voor de ProgressChanged gebeurtenis. Er is één toegevoegde eigenschap met de naam 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
    

Controlepunt 3

Op dit punt kunt u het onderdeel bouwen.

Uw onderdeel testen

  • Compileer het onderdeel.

    Het alles wat nog moet worden geschreven, zijn de methoden voor het starten en annuleren van asynchrone bewerkingen, CalculatePrimeAsync en CancelAsync.

De methoden Starten en Annuleren implementeren

U start de werkmethode op een eigen thread door de gemachtigde aan te roepen BeginInvoke die deze inpakt. Als u de levensduur van een bepaalde asynchrone bewerking wilt beheren, roept u de CreateOperation methode aan in de AsyncOperationManager helperklasse. Hiermee wordt een AsyncOperationgeretourneerd, die marshals aanroept op de gebeurtenis-handlers van de client naar de juiste thread of context.

U annuleert een bepaalde bewerking die in behandeling is door aan te roepen PostOperationCompleted op de bijbehorende AsyncOperation. Hiermee wordt deze bewerking beëindigd en eventuele volgende aanroepen naar de bewerking zullen AsyncOperation een uitzondering genereren.

Ga als volgende te werk om de functionaliteit Starten en Annuleren te implementeren:

  1. Implementeer de CalculatePrimeAsync methode. Zorg ervoor dat het door de client geleverde token (taak-id) uniek is ten opzichte van alle tokens die momenteel in behandeling zijnde taken vertegenwoordigen. Als de client een niet-uniek token doorgeeft, CalculatePrimeAsync genereert een uitzondering. Anders wordt het token toegevoegd aan de taak-id-verzameling.

    // 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. Implementeer de CancelAsync methode. Als de taskId parameter bestaat in de tokenverzameling, wordt deze verwijderd. Dit voorkomt dat geannuleerde taken die niet zijn gestart, worden uitgevoerd. Als de taak wordt uitgevoerd, wordt de BuildPrimeNumberList methode afgesloten wanneer wordt gedetecteerd dat de taak-id is verwijderd uit de levensduurverzameling.

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

Controlepunt 4

Op dit punt kunt u het onderdeel bouwen.

Uw onderdeel testen

  • Compileer het onderdeel.

Het PrimeNumberCalculator onderdeel is nu voltooid en klaar voor gebruik.

Zie How to: Implement a client of the Event-based Asynchronous Pattern (Een client implementeren met het op gebeurtenissen gebaseerde asynchrone patroon) voor een voorbeeldclient die gebruikmaakt van het onderdeel.PrimeNumberCalculator

Volgende stappen

U kunt dit voorbeeld invullen door te schrijven CalculatePrime, het synchrone equivalent van CalculatePrimeAsync de methode. Hierdoor wordt het PrimeNumberCalculator onderdeel volledig compatibel met het asynchrone patroon op basis van gebeurtenissen.

U kunt dit voorbeeld verbeteren door de lijst met alle priemgetallen te behouden die door verschillende aanroepen zijn gedetecteerd voor verschillende testnummers. Als u deze aanpak gebruikt, profiteert elke taak van het werk dat is uitgevoerd door vorige taken. Zorg ervoor dat u deze lijst beveiligt met lock regio's, zodat de toegang tot de lijst door verschillende threads wordt geserialiseerd.

U kunt dit voorbeeld ook verbeteren door te testen op triviale delers, zoals 2, 3 en 5.

Zie ook