Megosztás a következőn keresztül:


Útmutató: Az eseményalapú aszinkron mintát támogató összetevő implementálása

Ha olyan osztályt ír, amely néhány olyan művelettel rendelkezik, amelyek észrevehető késéseket okozhatnak, fontolja meg aszinkron funkciók megadását az eseményalapú aszinkron minta áttekintésének implementálásával.

Ez az útmutató bemutatja, hogyan hozhat létre olyan összetevőt, amely megvalósítja az eseményalapú aszinkron mintát. A rendszer a System.ComponentModel névtér segédosztályainak használatával valósítja meg, amely biztosítja, hogy az összetevő megfelelően működjön bármely alkalmazásmodellben, beleértve a ASP.NET, a konzolalkalmazásokat és a Windows Forms alkalmazásokat. Ez az összetevő egy vezérlővel és saját egyéni tervezőkkel PropertyGrid is tervezhető.

Amikor végzett, egy olyan alkalmazással fog rendelkezni, amely aszinkron módon számítja ki a prímszámokat. Az alkalmazás fő felhasználói felületi (UI) szálat és egy szálat fog használni minden egyes prímszámszámításhoz. Bár a nagy szám prímértékének tesztelése jelentős időt vehet igénybe, a fő felhasználói felületi szálat ez a késés nem szakítja meg, és az űrlap a számítások során válaszképes lesz. Egyszerre annyi számítást futtathat, amennyit csak szeretne, és szelektíven megszakíthatja a függőben lévő számításokat.

Az ebben az útmutatóban bemutatott feladatok a következők:

  • Az összetevő létrehozása

  • Nyilvános aszinkron események és meghatalmazottak meghatározása

  • Privát meghatalmazottak meghatározása

  • Nyilvános események megvalósítása

  • A befejezési módszer megvalósítása

  • A feldolgozói metódusok implementálása

  • Indítási és megszakítási módszerek implementálása

Ha a jelen témakörben szereplő kódot egyetlen listaelemként szeretné másolni, olvassa el a How to: Implement a Client of the Event-based Asynchronous Pattern (Útmutató: Az eseményalapú aszinkron minta ügyfélének implementálása) című témakört.

Az összetevő létrehozása

Első lépésként hozza létre azt az összetevőt, amely megvalósítja az eseményalapú aszinkron mintát.

Az összetevő létrehozása

  • Hozzon létre egy nevű PrimeNumberCalculator osztályt, amely a osztálytól Componentöröklődik.

Nyilvános aszinkron események és meghatalmazottak meghatározása

Az összetevő események használatával kommunikál az ügyfelekkel. A MethodNameCompleted esemény egy aszinkron feladat befejezésére figyelmezteti az ügyfeleket, a MethodNameProgressChanged esemény pedig tájékoztatja az ügyfeleket egy aszinkron feladat előrehaladásáról.

Aszinkron események definiálása az összetevő ügyfelei számára:

  1. Importálja a és System.Collections.Specialized a System.Threading névteret a fájl tetején.

    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. Az PrimeNumberCalculator osztálydefiníció előtt deklarálja a deklaráltakat a folyamat- és befejezési eseményekhez.

    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. Az osztálydefinícióban deklarálja az PrimeNumberCalculator eseményeket az előrehaladás és a befejezés ügyfeleknek való jelentéséhez.

    public event ProgressChangedEventHandler ProgressChanged;
    public event CalculatePrimeCompletedEventHandler CalculatePrimeCompleted;
    
    Public Event ProgressChanged _
        As ProgressChangedEventHandler
    Public Event CalculatePrimeCompleted _
        As CalculatePrimeCompletedEventHandler
    
  4. Az PrimeNumberCalculator osztálydefiníció után származtassa a CalculatePrimeCompletedEventArgs osztályt az egyes számítások eredményének jelentéséhez az ügyfél eseménykezelőjének az CalculatePrimeCompleted.event esetében. A tulajdonságokon kívül ez az AsyncCompletedEventArgs osztály lehetővé teszi az ügyfél számára, hogy meghatározza, milyen számot teszteltek, hogy prím, és mi az első osztó, ha nem prím.

    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
    

Ellenőrzőpont 1

Ezen a ponton létrehozhatja az összetevőt.

Az összetevő tesztelése

  • Fordítsa le az összetevőt.

    Két fordítói figyelmeztetés jelenik meg:

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

    Ezeket a figyelmeztetéseket a következő szakaszban töröljük.

Privát meghatalmazottak meghatározása

Az összetevő aszinkron aspektusai PrimeNumberCalculator belsőleg implementálva vannak egy speciális delegálttal, úgynevezett SendOrPostCallbacknéven. Az A SendOrPostCallback egy szálon ThreadPool végrehajtott visszahívási metódust jelöl. A visszahívási metódusnak olyan aláírással kell rendelkeznie, amely egyetlen típusú Objectparamétert használ, ami azt jelenti, hogy állapotot kell átadnia a burkolóosztályban lévő meghatalmazottak között. További információ: SendOrPostCallback.

Az összetevő belső aszinkron viselkedésének megvalósítása:

  1. Deklarálja és hozza létre a SendOrPostCallback meghatalmazottakat az PrimeNumberCalculator osztályban. Hozza létre az SendOrPostCallback objektumokat egy nevű InitializeDelegatessegédprogram-metódusban.

    Két meghatalmazottra lesz szüksége: egy az ügyfélnek küldött előrehaladás jelentéséhez, egy pedig az ügyfélnek a befejezés jelentéséhez.

    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. Hívja meg a metódust InitializeDelegates az összetevő konstruktorában.

    public PrimeNumberCalculator()
    {
        InitializeComponent();
    
        InitializeDelegates();
    }
    
    Public Sub New()
    
        InitializeComponent()
    
        InitializeDelegates()
    
    End Sub
    
  3. Deklaráljon egy delegáltat a PrimeNumberCalculator osztályban, amely az aszinkron módon elvégzendő tényleges munkát kezeli. Ez a delegált burkolja a munkavégző metódust, amely ellenőrzi, hogy egy szám prím. A delegált egy paramétert AsyncOperation használ az aszinkron művelet élettartamának nyomon követésére.

    private delegate void WorkerEventHandler(
        int numberToCheck,
        AsyncOperation asyncOp);
    
    Private Delegate Sub WorkerEventHandler( _
    ByVal numberToCheck As Integer, _
    ByVal asyncOp As AsyncOperation)
    
  4. Gyűjtemény létrehozása a függőben lévő aszinkron műveletek élettartamának kezeléséhez. Az ügyfélnek módot kell adnia a műveletek nyomon követésére a végrehajtásuk és a végrehajtásuk során, és ez a nyomon követés úgy történik, hogy az ügyfélnek egyedi jogkivonatot vagy feladatazonosítót kell átadnia, amikor az ügyfél az aszinkron metódusra hívja a hívást. Az PrimeNumberCalculator összetevőnek nyomon kell követnie az egyes hívásokat úgy, hogy társítja a tevékenységazonosítót a megfelelő hívással. Ha az ügyfél nem egyedi feladatazonosítót ad át, az PrimeNumberCalculator összetevőnek kivételt kell emelnie.

    Az PrimeNumberCalculator összetevő nyomon követi a feladatazonosítót egy nevű speciális gyűjteményosztály HybridDictionaryhasználatával. Az osztálydefinícióban hozzon létre egy HybridDictionary nevűt userStateToLifetime.

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

Nyilvános események megvalósítása

Az eseményalapú aszinkron mintát megvalósító összetevők események használatával kommunikálnak az ügyfelekkel. Ezek az események a megfelelő szálon vannak meghívva az AsyncOperation osztály segítségével.

Események létrehozása az összetevő ügyfeleinek:

  1. Nyilvános események implementálása az ügyfeleknek való jelentéskészítéshez. Egy eseményre lesz szüksége a jelentéskészítési folyamathoz, és egy eseményre a jelentés befejezéséhez.

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

A befejezési módszer megvalósítása

A befejezési delegált az a módszer, amelyet a mögöttes, szabadon szálba rendezett aszinkron viselkedés meghív, amikor az aszinkron művelet sikeres befejezéssel, hibával vagy megszakítással végződik. Ez a hívás egy tetszőleges szálon történik.

Ez a módszer távolítja el az ügyfél feladatazonosítóját az egyedi ügyféltokenek belső gyűjteményéből. Ez a metódus egy adott aszinkron művelet élettartamát is befejezi a metódusnak a PostOperationCompleted megfelelő AsyncOperationmetóduson való meghívásával. Ez a hívás az alkalmazásmodellnek megfelelő befejezési eseményt hoz létre a szálon. A metódus meghívása AsyncOperation után ez a PostOperationCompleted példány már nem használható, és a későbbi próbálkozások kivételt okoznak.

Az CompletionMethod aláírásnak tartalmaznia kell az aszinkron művelet eredményének leírásához szükséges összes állapotot. Az adott aszinkron művelet által tesztelt szám állapota, hogy a szám prímérték-e, és az első osztó értéke, ha nem prímszám. Az állapotot is tartalmazza, amely leírja az esetlegesen előforduló kivételeket, valamint az AsyncOperation adott tevékenységnek megfelelő állapotot.

Aszinkron művelet végrehajtása:

  • Implementálja a befejezési módszert. Hat paramétert vesz igénybe, amelyeket az ügyfél által visszaadott ügyfél feltöltésére CalculatePrimeCompletedEventArgsCalculatePrimeCompletedEventHandlerhasznál. Eltávolítja az ügyfél feladatazonosító-jogkivonatát a belső gyűjteményből, és az aszinkron művelet élettartamát a következő hívással szünteti meg PostOperationCompleted: . A AsyncOperation rendszer az alkalmazásmodellnek megfelelő szálra vagy környezetre hívja a hívást.

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

Ellenőrzőpont 2

Ezen a ponton létrehozhatja az összetevőt.

Az összetevő tesztelése

  • Fordítsa le az összetevőt.

    Egy fordítói figyelmeztetést fog kapni:

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

    Ez a figyelmeztetés a következő szakaszban lesz feloldva.

A feldolgozói metódusok implementálása

Eddig implementálta a támogató aszinkron kódot az PrimeNumberCalculator összetevőhöz. Most már implementálhatja azt a kódot, amely elvégzi a tényleges munkát. Három módszert fog implementálni: CalculateWorker, BuildPrimeNumberListés IsPrime. Együtt, BuildPrimeNumberList és IsPrime áll egy jól ismert algoritmus úgynevezett Sieve of Eratosthenes, amely meghatározza, hogy egy szám prím, hogy megtalálja az összes prím számot egészen a négyzet gyökere a tesztszám. Ha ennél a ponttól nem találhatók osztók, a vizsgálati szám prím.

Ha ez az összetevő a maximális hatékonyság érdekében lett volna megírva, emlékezni fog a különböző tesztszámok különböző hívásai által felfedezett összes prímszámra. Emellett az olyan triviális osztókat is ellenőrzi, mint a 2, a 3 és az 5. Ennek a példának az a célja, hogy bemutassa, hogyan hajthatók végre az időigényes műveletek aszinkron módon, így ezek az optimalizálások gyakorlatként maradnak.

A CalculateWorker metódus egy delegáltba van burkolva, és aszinkron módon meghívja a metódust a hívásával BeginInvoke.

Megjegyzés

A folyamatjelentés a metódusban van implementálva BuildPrimeNumberList . A gyors számítógépeken ProgressChanged az események gyors egymásutánban hozhatók létre. Az ügyfélszálnak, amelyen ezek az események létrejönnek, képesnek kell lennie kezelni ezt a helyzetet. Előfordulhat, hogy a felhasználói felület kódját elárasztják az üzenetek, és nem tudnak lépést tartani, ami nem válaszol. Az ezt a helyzetet kezelő felhasználói felületre példa : How to: Implement a Client of the Event-based Asynchronous Pattern (Útmutató: Az eseményalapú aszinkron minta ügyfélének implementálása).

A prímszám számításának aszinkron végrehajtása:

  1. Implementálja a TaskCanceled segédprogram metódusát. Ez ellenőrzi a tevékenység élettartam-gyűjteményét a megadott tevékenységazonosítóhoz, és visszaadja true , ha a tevékenységazonosító nem található.

    // 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. Implementálja a metódust CalculateWorker . Két paramétert vesz igénybe: a tesztelni kívánt számot és egy paramétert 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. Implementálja a BuildPrimeNumberListparancsot. Két paramétert vesz igénybe: a tesztelni kívánt számot és egy paramétert AsyncOperation. A használatával jelentést készít az AsyncOperation előrehaladásról és a növekményes eredményekről. Ez biztosítja, hogy az ügyfél eseménykezelői az alkalmazásmodell megfelelő szálán vagy környezetében legyenek meghívva. Amikor BuildPrimeNumberList egy elsődleges számot talál, ezt növekményes eredményként jelenti az ügyfél eseménykezelőjének az ProgressChanged eseményhez. Ehhez egy , nevű CalculatePrimeProgressChangedEventArgsosztályra ProgressChangedEventArgsvan szükség, amelynek egy nevű hozzáadott tulajdonsága LatestPrimeNumbervan.

    A BuildPrimeNumberList metódus rendszeres időközönként meghívja a metódust TaskCanceled , és kilép, ha a metódus visszatér 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. Implementálja a IsPrimeparancsot. Három paramétert vesz igénybe: az ismert prímszámok listáját, a tesztelni kívánt számot és az első megtalált osztó kimeneti paraméterét. A prímszámok listája alapján meghatározza, hogy a tesztszám prímszám-e.

    // 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. Származtatás CalculatePrimeProgressChangedEventArgs : ProgressChangedEventArgs. Ez az osztály szükséges ahhoz, hogy növekményes eredményeket jelentsen az ügyfél eseménykezelőjének az ProgressChanged eseményhez. Egy nevű hozzáadott tulajdonsága LatestPrimeNumbervan.

    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
    

Ellenőrzőpont 3

Ezen a ponton létrehozhatja az összetevőt.

Az összetevő tesztelése

  • Állítsa össze az összetevőt.

    Csak az aszinkron műveletek indítására és megszakítására használható módszereket kell írni, CalculatePrimeAsync és CancelAsynca .

A Start és a Mégse metódus implementálása

A munkavégző metódust a saját szálán indíthatja el a körbefuttatást végző meghatalmazott meghívásával BeginInvoke . Egy adott aszinkron művelet élettartamának kezeléséhez hívja meg a metódust CreateOperation a AsyncOperationManager segédosztályon. Ez egy AsyncOperation, amely a marsallok meghívja az ügyfél eseménykezelőit a megfelelő szálra vagy környezetbe.

Egy adott függőben lévő műveletet a megfelelő AsyncOperationhívásával PostOperationCompleted szakíthat meg. Ez véget vet a műveletnek, és az azt követő hívások AsyncOperation kivételt okoznak.

A Start és a Mégse funkció implementálása:

  1. Implementálja a metódust CalculatePrimeAsync . Győződjön meg arról, hogy az ügyfél által megadott jogkivonat (tevékenységazonosító) egyedi a jelenleg függőben lévő tevékenységeket képviselő összes jogkivonat tekintetében. Ha az ügyfél nem egyedi jogkivonatot ad át, CalculatePrimeAsync kivételt jelez. Ellenkező esetben a rendszer hozzáadja a jogkivonatot a feladatazonosító-gyűjteményhez.

    // 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. Implementálja a metódust CancelAsync . Ha a taskId paraméter megtalálható a jogkivonatgyűjteményben, a rendszer eltávolítja. Ez megakadályozza azokat a megszakított feladatokat, amelyek nem kezdődtek el. Ha a feladat fut, a BuildPrimeNumberList metódus kilép, amikor azt észleli, hogy a tevékenységazonosító el lett távolítva az élettartam-gyűjteményből.

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

Ellenőrzőpont 4

Ezen a ponton létrehozhatja az összetevőt.

Az összetevő tesztelése

  • Állítsa össze az összetevőt.

Az PrimeNumberCalculator összetevő elkészült, és használatra kész.

Az összetevőt használó PrimeNumberCalculator példaügyfél esetében lásd : How to: Implement a Client of the Event-based Asynchronous Pattern (Útmutató: Az eseményalapú aszinkron minta ügyfele implementálása).

Következő lépések

Ezt a példát a metódus szinkron megfelelőjének megírásával CalculatePrimetöltheti CalculatePrimeAsync ki. Így az PrimeNumberCalculator összetevő teljes mértékben megfelel az eseményalapú aszinkron mintának.

Ezt a példát úgy javíthatja, hogy megőrzi a különböző tesztszámok különböző hívásai által felderített prímszámok listáját. Ezzel a megközelítéssel minden tevékenységnek hasznára válik az előző tevékenységek által végzett munka. Ügyeljen arra, hogy a listát régiókkal lock védje, ezért a különböző szálak által a listához való hozzáférés szerializálva van.

Ezt a példát a triviális osztók (például 2, 3 és 5) tesztelésével is javíthatja.

Lásd még