Compartilhar via


Demonstra Passo a passo: Um componente que oferece suporte ao implementar o padrão assíncrono baseado em evento

Se estiver escrevendo uma classe com algumas operações que podem incorrer atrasos perceptíveis, considere a possibilidade de dar a ele funcionalidade assíncrono, Implementando o padrão assíncrono baseado em evento Visão geral sobre.

Esta explicação passo a passo ilustra como criar um componente que implementa o padrão assíncrono baseado em evento.Ele é implementado através de classes auxiliares do System.ComponentModel namespace, que garante que o componente funciona corretamente em qualquer modelo de aplicativo, incluindo ASP.NETAplicativos Windows Forms e do console. Este componente também é projetáveis com um PropertyGrid controle e seus próprios designers personalizados.

Quando terminar, você terá um aplicativo que calcula números primos de forma assíncrona.O aplicativo terá um segmento de interface de usuário principal e um thread para cada número primo cálculo.Embora teste se é um grande número primos podem levar uma considerável quantidade de time, o thread da interface do usuário principal não será interrompido por esse atraso e o formulário será responsivo durante os cálculos.Você poderá executar sistema autônomo muitos cálculos sistema autônomo você que seletivamente e simultaneamente Cancelar pendentes cálculos.

Tarefas ilustradas nesta explicação passo a passo incluem:

  • Criando o componente

  • Definindo eventos assíncrono do públicos e delegados

  • Definir representantes particular

  • Implementar eventos públicos

  • Implementar o método de conclusão

  • Implementar os métodos de trabalho

  • Implementando Start e cancelar métodos

Para copiar o código deste tópico como uma única lista, consulte Como: Um componente que oferece suporte ao implementar o padrão assíncrono baseado em evento.

Criando o componente

A primeira etapa é criar o componente que implementará a padrão assíncrono baseado em evento.

Para criar o componente

  • Criar uma classe chamada PrimeNumberCalculator que herda de Component.

Definindo eventos assíncrono do públicos e delegados

O componente comunica-se aos clientes usar eventos.The MethodNameCompleted evento alertas de clientes para a conclusão de uma tarefa assíncrono e a MethodNameProgressChanged evento informa os clientes o progresso de uma tarefa assíncrono.

Para definir eventos assíncrono para clientes do seu componente:

  1. Importar o System.Threading e System.Collections.Specialized espaços para nome na parte superior do seu arquivo.

    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
    
    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;
    
    import System.*;
    import System.Collections.*;
    import System.Collections.Specialized.*;
    import System.ComponentModel.*;
    import System.Data.*;
    import System.Drawing.*;
    import System.Threading.*;
    import System.Windows.Forms.*;
    
    
  2. Antes do PrimeNumberCalculator classe de definição, declarar delegados para eventos de andamento e a conclusão.

    Public Delegate Sub ProgressChangedEventHandler( _
        ByVal e As ProgressChangedEventArgs)
    
    Public Delegate Sub CalculatePrimeCompletedEventHandler( _
        ByVal sender As Object, _
        ByVal e As CalculatePrimeCompletedEventArgs)
    
    public delegate void ProgressChangedEventHandler(
        ProgressChangedEventArgs e);
    
    public delegate void CalculatePrimeCompletedEventHandler(
        object sender,
        CalculatePrimeCompletedEventArgs e);
    
    public delegate void ProgressChangedEventHandler(ProgressChangedEventArgs e);
    
    /** @delegate 
     */
    public delegate void CalculatePrimeCompletedEventHandler(Object sender, 
        CalculatePrimeCompletedEventArgs e);
    
  3. No PrimeNumberCalculator definição de classe, declare eventos por relatar o andamento e a conclusão a clientes.

    Public Event ProgressChanged _
        As ProgressChangedEventHandler
    Public Event CalculatePrimeCompleted _
        As CalculatePrimeCompletedEventHandler
    
    public event ProgressChangedEventHandler ProgressChanged;
    public event CalculatePrimeCompletedEventHandler CalculatePrimeCompleted;
    
    public ProgressChangedEventHandler progressChanged = null;
    /** @event 
     */
    public void add_ProgressChanged(ProgressChangedEventHandler p)
    {
        progressChanged =(ProgressChangedEventHandler)System.Delegate.
            Combine(progressChanged, p);
    } 
    
    /** @event 
     */
    public void remove_ProgressChanged(ProgressChangedEventHandler p)
    {
        progressChanged =(ProgressChangedEventHandler)System.Delegate.
            Remove(progressChanged, p);
    } 
    
    public CalculatePrimeCompletedEventHandler calculatePrimeCompleted = null;
    /** @event 
     */
    public void add_CalculatePrimeCompleted(CalculatePrimeCompletedEventHandler p)
    {
        calculatePrimeCompleted =(CalculatePrimeCompletedEventHandler)
            System.Delegate.Combine(calculatePrimeCompleted, p);
    } 
    
    /** @event 
     */
    public void remove_CalculatePrimeCompleted(
        CalculatePrimeCompletedEventHandler p)
    {
        calculatePrimeCompleted =(CalculatePrimeCompletedEventHandler)
            System.Delegate.Remove(calculatePrimeCompleted, p);
    } 
    
  4. Após o PrimeNumberCalculator definição da classe, derivar o CalculatePrimeCompletedEventArgs classe por relatar o resultado de cada cálculo ao manipulador de eventos do cliente para o CalculatePrimeCompleted.evento. Juntamente com o AsyncCompletedEventArgs Propriedades, essa classe permite que o cliente determinar que número foi testado, seja preparam e o que o primeiro divisor é se ela não for principal.

    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
    
    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 extends AsyncCompletedEventArgs
    {
        private int numberToTestValue = 0;
        private int firstDivisorValue = 1;
        private boolean isPrimeValue;
    
        public CalculatePrimeCompletedEventArgs(int numberToTest, int firstDivisor,
            boolean isPrime, System.Exception e, boolean cancelled, Object state)
        {
            super(e, cancelled, state);
            this.numberToTestValue = numberToTest;
            this.firstDivisorValue = firstDivisor;
            this.isPrimeValue = isPrime;
        } 
    
        /** @property 
         */
        public int get_NumberToTest()
        {
            // Raise an exception if the operation failed or 
            // was cancelled.
            RaiseExceptionIfNecessary();
    
            // If the operation was successful, return the 
            // property value.
            return numberToTestValue;
        }
    
        /** @property 
         */
        public int get_FirstDivisor()
        {
            // Raise an exception if the operation failed or 
            // was cancelled.
            RaiseExceptionIfNecessary();
    
            // If the operation was successful, return the 
            // property value.
            return firstDivisorValue;
        }
    
        /** @property 
         */
        public boolean get_IsPrime()
        {
            // Raise an exception if the operation failed or 
            // was cancelled.
            RaiseExceptionIfNecessary();
    
            // If the operation was successful, return the 
            // property value.
            return isPrimeValue;
        } 
    } 
    

Ponto de Verificação

Neste ponto, você pode criar o componente.

Para testar o componente

  • compilar o componente.

    Você receberá dois avisos do compilador:

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

    Esses avisos serão apagados na próxima seção.

Definir representantes particular

sistema autônomo aspectos do assíncrono PrimeNumberCalculator componente são implementadas internamente com um delegado especial conhecido sistema autônomo um SendOrPostCallback. A SendOrPostCallback representa um método de retorno de chamada que executa em um ThreadPool segmento. O método de retorno de chamada deve ter uma assinatura que usa um único parâmetro do tipo Object, que significa que você precisará passar estado entre representantes em uma classe wrapper. Para obter mais informações, consulte SendOrPostCallback.

Para implementar comportamento assíncrono interna do seu componente:

  1. Declarar e criar o SendOrPostCallback delega na PrimeNumberCalculator classe. criar the SendOrPostCallback objetos em um método de utilitário chamado InitializeDelegates.

    Você precisará de dois delegados: uma para relatar o andamento para o cliente e um para o relatório de conclusão para o cliente.

    Private onProgressReportDelegate As SendOrPostCallback
    Private onCompletedDelegate As SendOrPostCallback
    
    
    ...
    
    
    Protected Overridable Sub InitializeDelegates()
        onProgressReportDelegate = _
            New SendOrPostCallback(AddressOf ReportProgress)
        onCompletedDelegate = _
            New SendOrPostCallback(AddressOf CalculateCompleted)
    End Sub
    
    private SendOrPostCallback onProgressReportDelegate;
    private SendOrPostCallback onCompletedDelegate;
    
    
    ...
    
    
    protected virtual void InitializeDelegates()
    {
        onProgressReportDelegate =
            new SendOrPostCallback(ReportProgress);
        onCompletedDelegate =
            new SendOrPostCallback(CalculateCompleted);
    }
    
    private SendOrPostCallback onProgressReportDelegate;
    private SendOrPostCallback onCompletedDelegate;
    
    
    ...
    
    
    protected void InitializeDelegates()
    {
        onProgressReportDelegate = new SendOrPostCallback(ReportProgress);
        onCompletedDelegate = new SendOrPostCallback(CalculateCompleted);
    } 
    
    
  2. Chamar o InitializeDelegates método no construtor do seu componente.

    Public Sub New()
    
        InitializeComponent()
    
        InitializeDelegates()
    
    End Sub
    
    public PrimeNumberCalculator()
    {   
        InitializeComponent();
    
        InitializeDelegates();
    }
    
    public PrimeNumberCalculator()
    {
        ///
        /// Required for Windows.Forms Class Composition Designer support
        ///
        InitializeComponent();
        InitializeDelegates();
    } 
    
  3. Declarar um delegado no PrimeNumberCalculator classe que manipula o trabalho real seja concluído assincronamente. Esse delegado quebra o método de trabalho que testa se um número é "Prime".O delegado assume um AsyncOperation parâmetro, que será usado para controlar o tempo de vida da operação assíncrono.

    Private Delegate Sub WorkerEventHandler( _
    ByVal numberToCheck As Integer, _
    ByVal asyncOp As AsyncOperation)
    
    private delegate void WorkerEventHandler(
        int numberToCheck,
        AsyncOperation asyncOp);
    
        private delegate void WorkerEventHandler(
            int numberToCheck, 
            AsyncOperation asyncOp );
    
  4. criar uma coleção para gerenciar as vidas úteis de operações assíncrono pendentes.O cliente precisa de uma forma para rastrear sistema autônomo operações são executadas e concluídos e esse acompanhamento é feita por exigir que o cliente a passar de um símbolo exclusivo ou identificação da tarefa, quando o cliente faz a telefonar ao método assíncrono.The PrimeNumberCalculator componente deve manter o controle de cada telefonar, associando a identificação da tarefa sua invocação correspondente. Se o cliente passar uma identificação da tarefa que não é exclusiva, o PrimeNumberCalculator componente deve disparar uma exceção.

    The PrimeNumberCalculator componente mantém controle da identificação da tarefa usando uma classe de coleção especial chamada um HybridDictionary. Na definição de classe, crie um HybridDictionary chamado userTokenToLifetime.

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

Implementar eventos públicos

Componentes que implementam o padrão assíncrono com base em eventos se comunicam com clientes que usam eventos.Esses eventos são invocados no thread correto com a Ajuda do AsyncOperation classe.

Para gerar eventos para os clientes do seu componente:

  • Implemente eventos públicos para a criação de relatórios para clientes.Você precisará de um evento por relatar o andamento e outro para o relatório de conclusão.

    ' 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 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);
        }
    }
    
    // You are guaranteed to be on the correct thread, as
    // this method is invoked via the AsyncOperation object.
    private void CalculateCompleted(Object operationState)
    {
        CalculatePrimeCompletedEventArgs e = 
            (CalculatePrimeCompletedEventArgs)operationState;
        OnCalculatePrimeCompleted(e);
    } 
    
    // You are guaranteed to be on the correct thread, as
    // this method is invoked via the AsyncOperation object.
    private void ReportProgress(Object state)
    {
        ProgressChangedEventArgs e = (ProgressChangedEventArgs)state;
        OnProgressChanged(e);
    } 
    
    protected void OnCalculatePrimeCompleted(CalculatePrimeCompletedEventArgs e)
    {
        if (calculatePrimeCompleted != null) {
            calculatePrimeCompleted.Invoke(this, e);
        }
    } 
    
    protected void OnProgressChanged(ProgressChangedEventArgs e)
    {
        if (progressChanged != null) {
            progressChanged.Invoke(e);
        }
    } 
    

Implementar o método de conclusão

O delegado de conclusão é o método que invocará o comportamento assíncrono subjacente, segmentação disponível quando termina a operação assíncrona, conclusão bem-sucedida, erro ou cancelamento.Essa chamada acontece em um thread arbitrário.

Esse método é onde a identificação da tarefa do cliente é removida da coleção interna de tokens de cliente exclusivo.Esse método também termina a tempo de vida de uma determinada operação assíncrono chamando o PostOperationCompleted método no correspondente AsyncOperation. Essa telefonar dispara o evento de conclusão no thread que é apropriado para o modelo de aplicativo.Após o PostOperationCompleted método é chamado, esta instância do AsyncOperation não pode mais ser usado, e qualquer tentativa subseqüente para usá-lo lançará uma exceção.

The CompletionMethod assinatura deverá manter todos os estados necessários para descrever o resultado da operação assíncrono. Ele contém o estado para o número que foi testado por esta operação assíncrono específica, se o número é "Prime" e o valor do divisor seu primeiro se não for um número primo.Ele também mantém o estado descrever qualquer exceção que ocorreu e o AsyncOperation correspondente a essa tarefa específica.

Para concluir uma operação assíncrono:

  • Implemente o método de conclusão.Leva seis parâmetros, ele usa para popular um CalculatePrimeCompletedEventArgs que é retornado ao cliente por meio do cliente CalculatePrimeCompletedEventHandler. Ela remove o token de identificação da tarefa do cliente da coleção interna e termina tempo de vida da operação assíncrono com uma telefonar para PostOperationCompleted. The AsyncOperation empacota a telefonar para o thread ou contexto apropriado para o modelo de aplicativo.

    ' 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
    
    // 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 void CompletionMethod(
            int numberToTest,
            int firstDivisor,
            boolean isPrime,
            System.Exception exception,
            boolean canceled,
            AsyncOperation asyncOp)
        {
            // If the task was not previously canceled,
            // remove the task from the lifetime collection.
            if (!canceled)
            {
                synchronized (userStateToLifetime.get_SyncRoot())
                {
                    userStateToLifetime.Remove(asyncOp.get_UserSuppliedState());
                }
            }
    
            // Package the results of the operation in a 
            // CalculatePrimeCompletedEventArgs.
            CalculatePrimeCompletedEventArgs e =
                new CalculatePrimeCompletedEventArgs(
                numberToTest,
                firstDivisor,
                isPrime,
                exception,
                canceled,
                asyncOp.get_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.
    
        }
    
        // 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.
    

Ponto de Verificação

Neste ponto, você pode criar o componente.

Para testar o componente

  • compilar o componente.

    Você receberá um aviso do compilador:

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

    Esse aviso será resolvido na próxima seção.

Implementar os métodos de trabalho

Até agora, você implementou o suporte código assíncrono para o PrimeNumberCalculator componente. Agora você pode implementar o código que faz o trabalho real.Você irá implementar três métodos: CalculateWorker, BuildPrimeNumberList, e IsPrime. Juntos, BuildPrimeNumberList e IsPrime compreendem um algoritmo bem conhecido chamado o Sieve de Eratosthenes, que determina se é um número primo, localizando todos os números de "Prime" backup para a raiz quadrada do número de teste. Não se forem encontrados nenhum divisors por esse ponto, o número de teste é "Prime".

Se esse componente foram escrito para máxima eficiência, ele seria Lembre-se todos os números primos descobertos por várias chamadas para números diferentes de teste.Ele também deve verificar divisors triviais como 2, 3 e 5.A intenção deste exemplo é demonstrar sistema autônomo demoradas operações podem ser executadas assincronamente, no entanto, para que essas otimizações são deixadas sistema autônomo um exercício para você.

The CalculateWorker método é empacotado em um delegado e é chamado de forma assíncrona com uma telefonar para BeginInvoke.

Observação:

Relatório de andamento é implementado na BuildPrimeNumberList método. Em computadores rápidos, ProgressChanged eventos podem ser gerados em sucessão rápida. O segmento do cliente, em que esses eventos são disparados, deve ser capaz de lidar com essa situação.Código de interface de usuário pode estar inundado com mensagens e não pode acompanhar, resultando em deslocado comportamento.Para uma interface de usuário de exemplo que manipula esta situação, consulte Como: Implementar um cliente do padrão assíncrono baseado em evento.

Para executar o cálculo do número primo assíncrona:

  1. Implementar o TaskCanceled método de utilitário. Isso verifica a coleção de tempo de vida da tarefa para a identificação da tarefa determinada e retorna true Se a identificação da tarefa não for encontrada.

    ' 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
    
    // 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 boolean TaskCanceled(Object taskId)
        {
            return (!userStateToLifetime.Contains(taskId) );
        }
    
  2. Implemente o método CalculateWorker.Ele usa dois parâmetros: um número para teste e um AsyncOperation.

    ' 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 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);
    }
    
        private void CalculateWorker(
            int numberToTest,
            AsyncOperation asyncOp)
        {
            boolean isPrime = false;
            int firstDivisor = 1;
            System.Exception exception = null;
    
            // Check that the task is still active.
            // The operation may have been canceled before
            // the thread was scheduled.
            if (!TaskCanceled(asyncOp.get_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 (System.Exception ex)
                {
                    exception = ex;
                }
            }
    
            this.CompletionMethod(
                numberToTest,
                firstDivisor,
                isPrime,
                exception,
                TaskCanceled(asyncOp.get_UserSuppliedState()),
                asyncOp);
        }
    
  3. Implemente BuildPrimeNumberList.Ele usa dois parâmetros: o número de teste e um AsyncOperation. Ele usa o AsyncOperation para relatar o andamento e resultados incrementais. Isso garante que os manipuladores de eventos do cliente são chamados no thread correto ou contexto para o modelo de aplicativo.Quando BuildPrimeNumberList Localiza um número primo, ele reporta isso sistema autônomo um resultado incremental ao manipulador de eventos do cliente para o ProgressChanged evento. Isso requer uma classe derivada de ProgressChangedEventArgs, chamado CalculatePrimeProgressChangedEventArgs, que tenha um adicionado propriedade chamada LatestPrimeNumber.

    The BuildPrimeNumberList método periodicamente chama o TaskCanceled método e sai, se o método retornar true.

    ' 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 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 ArrayList BuildPrimeNumberList(int numberToTest, 
            AsyncOperation asyncOp)
        {
            ProgressChangedEventArgs e = null;
            ArrayList primes = new ArrayList();
            int firstDivisor = 1;
            int n = 5;
    
            // Add the first prime numbers.
            primes.Add((Int32)2);
            primes.Add((Int32)3);
    
            // Do the work.
            while (n < numberToTest &&
                   !TaskCanceled( asyncOp.get_UserSuppliedState()) )
            {
                if (IsPrime(primes, n, /** @out */firstDivisor)) 
                {
                    // Report to the client that you found a prime.
                    e = new CalculatePrimeProgressChangedEventArgs(n, 
                        (int)((float)n / ( float)(numberToTest) * 100), 
                        asyncOp.get_UserSuppliedState());
    
                    asyncOp.Post(this.onProgressReportDelegate, e);
    
                    primes.Add((Int32)n);
    
                    // Yield the rest of this time slice.
                    System.Threading.Thread.Sleep(0);
                }
    
                // Skip even numbers.
                n += 2;
    
            }
            return primes;
        } 
    
  4. Implemente IsPrime.Ele usa três parâmetros: uma lista dos números primos conhecidos, o número para testar e um parâmetro de saída para o divisor primeiro encontrado.Dada a lista dos números primos, ele determina se o número de teste é "Prime".

    ' 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 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 boolean IsPrime(
            ArrayList primes, 
            int n, 
            /** @ref */int firstDivisor)
        {
            boolean foundDivisor = false;
            boolean 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.get_Count() && !foundDivisor && !exceedsSquareRoot) 
            {
                // The divisor variable will be the smallest 
                // prime number not yet tried.
                divisor = Convert.ToInt32(primes.get_Item(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;
        } 
    
  5. Derivar CalculatePrimeProgressChangedEventArgs de ProgressChangedEventArgs. Essa classe é necessária para emissão de relatórios resultados incrementais para manipulador de eventos do cliente para o ProgressChanged evento. Ele tem uma propriedade adicional chamada LatestPrimeNumber.

    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 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 
        extends ProgressChangedEventArgs
    {
        private int latestPrimeNumberValue = 1;
    
        public CalculatePrimeProgressChangedEventArgs(int latestPrime, 
            int progressPercentage, Object userToken)
        {
            super(progressPercentage, userToken);
            this.latestPrimeNumberValue = latestPrime;
        }
    
        /** @property 
         */
        public int get_LatestPrimeNumber()
        {
            return latestPrimeNumberValue;
        } 
    }
    

Ponto de Verificação

Neste ponto, você pode criar o componente.

Para testar o componente

  • compilar o componente.

    Tudo o que continua a serem gravados é os métodos para iniciar e cancelar operações assíncrono, CalculatePrimeAsync e CancelAsync.

Implementando Start e cancelar métodos

Iniciar o método do operador em seu próprio thread chamando BeginInvoke sobre o delegado que encapsula a ele. Para gerenciar o tempo de vida de uma determinada operação assíncrono, chame o CreateOperation método na AsyncOperationManager classe auxiliar. Isso retorna um AsyncOperation, que empacota chamadas do cliente evento manipuladores no thread correto ou contexto.

Cancelar uma determinada operação pendente, chamando PostOperationCompleted no seu correspondente AsyncOperation. Aqui termina a operação e todas as chamadas subseqüentes para seus AsyncOperation lançará uma exceção.

Implementar Start e cancelar funcionalidade:

  1. Implemente o método CalculatePrimeAsync.Certifique-se de que o token fornecido pelo cliente (identificação da tarefa) é exclusivo com relação a todos os símbolos que representam atualmente tarefas pendentes.Se o cliente passar um token, não-exclusivo CalculatePrimeAsync gera uma exceção. Caso contrário, o token é adicionado à coleção de identificação da tarefa.

    ' 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
    
    // 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 void CalculatePrimeAsync(int numberToTest, Object taskId)
        {
            // Create an AsyncOperation for taskId.
            AsyncOperation asyncOp = AsyncOperationManager.CreateOperation(taskId);
            synchronized (userStateToLifetime.get_SyncRoot()) 
            {
                // Multiple threads will access the task dictionary,
                // so it must be locked to serialize access.
                if (userStateToLifetime.Contains(taskId)) {
                    throw new ArgumentException("Task ID parameter must be unique", 
                        "taskId");
                }
    
                userStateToLifetime.set_Item(taskId, asyncOp);
            }
    
            // Start the asynchronous operation.
            WorkerEventHandler workerDelegate = new WorkerEventHandler(CalculateWorker);
            workerDelegate.BeginInvoke(
                numberToTest, 
                asyncOp, 
                null, 
                null);
        } 
    
  2. Implemente o método CancelAsync.Se o taskId parâmetro existe na coleção de token, ele será removido. Isso impede que cancelado tarefas não iniciadas sejam executados.Se a tarefa estiver em execução, a BuildPrimeNumberList método será encerrado quando ele detecta que a identificação da tarefa foi removida da coleção de tempo de vida.

    ' 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 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 void CancelAsync(Object taskId)
        {
            Object obj = userStateToLifetime.get_Item(taskId);
            if (obj != null)
            {
                synchronized (userStateToLifetime.get_SyncRoot())
                {
                    userStateToLifetime.Remove(taskId);
                }
            }
        }
    

Ponto de Verificação

Neste ponto, você pode criar o componente.

Para testar o componente

  • compilar o componente.

The PrimeNumberCalculator componente agora está concluído e pronto para ser usado.

Para um cliente de exemplo que usa o PrimeNumberCalculatorcomponente, consulte Como: Implementar um cliente do padrão assíncrono baseado em evento.

Próximas etapas

Este exemplo podem preencher escrevendo CalculatePrime, o equivalente a síncrono CalculatePrimeAsync método. Isso fará com que o PrimeNumberCalculator totalmente compatível com o padrão assíncrono com base em eventos de componente.

Você pode melhorar este exemplo mantendo a lista de todos os números primos descobertas por várias chamadas para números diferentes de teste.Usando essa abordagem, cada tarefa será beneficiada com o trabalho concluído pelas tarefas anteriores.Tenha cuidado para proteger esta lista com lock regiões, para que o acesso à lista por threads diferentes é serializado.

Você também pode melhorar este exemplo testando divisors triviais, como 2, 3 e 5.

Consulte também

Tarefas

Como: Executar uma operação no plano de fundo

Como: Um componente que oferece suporte ao implementar o padrão assíncrono baseado em evento

Conceitos

padrão assíncrono baseado em evento Visão geral sobre

Outros recursos

Multisegmentação no Visual Basic

Vários segmentos de programação com o padrão assíncrono baseado em eventoo