Demonstra Passo a passo: A implementação de um componente que suporta o padrão assíncrono baseado em evento
Se você estiver escrevendo uma classe com algumas operações que podem estar sujeitas a atrasos perceptíveis, considere a possibilidade de dar a ele funcionalidade asynchronous Implementando o Event-based Asynchronous Pattern Overview.
Esta explicação passo a passo ilustra como criar um componente que implementa o padrão assíncrono baseado em eventos. Ele é implementado usando classes do auxiliar da System.ComponentModel namespace, que garante que o componente funciona corretamente em qualquer modelo de aplicativo, incluindo ASP.NET, aplicativos de Console e aplicativos do Windows Forms. Este componente também é projetáveis com um PropertyGrid o controle e seus próprios designers personalizados.
Quando terminar, você terá um aplicativo que calcula os números primos de forma assíncrona. Seu aplicativo terá um segmento de interface (UI) do usuário principal e um thread para cada número primo de cálculo. Embora seja um grande número de teste prime pode levar uma quantidade considerável de tempo, o principal segmento de interface do usuário não será interrompido por esse atraso e o formulário ser ágil durante os cálculos. Você será capaz de executar como muitos cálculos como você que simultaneamente e seletivamente Cancelar pendentes cálculos.
As tarefas ilustradas neste passo a passo incluem:
Criando o componente.
Definição de delegados e eventos assíncronos públicos
Definindo particular delegados
A implementação de eventos públicos
Implementando o método de conclusão
Implementar os métodos de trabalho
A implementação de início e Cancelar
Para copiar o código deste tópico como uma única lista, consulte Como: Implementar um componente que suporta o padrão assíncrono baseado em evento.
Criando o componente.
A primeira etapa é criar o componente que implementará o padrão assíncrono baseado em eventos.
Para criar o componente
- Criar uma classe chamada PrimeNumberCalculator que herda de Component.
Definição de delegados e eventos assíncronos públicos
Seu componente se comunica usando eventos de clientes. O MethodNameCompleted eventos de clientes para a conclusão de uma tarefa assíncrona, alertas e o MethodNameProgressChanged evento informa os clientes o progresso de uma tarefa assíncrona.
Para definir eventos assíncronos para clientes do seu componente:
Importação de System.Threading e System.Collections.Specialized namespaces no topo do 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;
Antes de PrimeNumberCalculator definição de classe, 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);
No PrimeNumberCalculator definição de classe, declare os eventos por relatar o andamento e a conclusão para os clientes.
Public Event ProgressChanged _ As ProgressChangedEventHandler Public Event CalculatePrimeCompleted _ As CalculatePrimeCompletedEventHandler
public event ProgressChangedEventHandler ProgressChanged; public event CalculatePrimeCompletedEventHandler CalculatePrimeCompleted;
Após a PrimeNumberCalculator definição de classe, derivar o CalculatePrimeCompletedEventArgs classe para relatar o resultado de cada cálculo para o manipulador de eventos do cliente para o CalculatePrimeCompleted.event. Além de AsyncCompletedEventArgs Propriedades, essa classe permite que o cliente determinar qual número foi testado, seja desobstrução, e qual é o divisor primeiro se é prime.
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; } } }
Ponto de Verificação
Neste ponto, você pode criar o componente.
Para testar seu componente.
Compile 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.
Definindo particular delegados
Os aspectos assíncronos a PrimeNumberCalculator componente são implementadas internamente com um delegado especial conhecido como 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, o que significa que você precisará passar o estado entre representantes em uma classe de wrapper. Para obter mais informações, consulte SendOrPostCallback.
Para implementar o comportamento de assíncrono interno do componente:
Declarar e criar o SendOrPostCallback delega na PrimeNumberCalculator classe. Criar o 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); }
Chamar o InitializeDelegates método no construtor do. seu componente
Public Sub New() InitializeComponent() InitializeDelegates() End Sub
public PrimeNumberCalculator() { InitializeComponent(); InitializeDelegates(); }
Declare um representante do PrimeNumberCalculator classe que manipula o trabalho real a ser feito de forma assíncrona. Este representante engloba o método de trabalho que testa se um número é um "Prime". O delegate assume um AsyncOperation parâmetro, que será usado para controlar o tempo de vida da operação assíncrona.
Private Delegate Sub WorkerEventHandler( _ ByVal numberToCheck As Integer, _ ByVal asyncOp As AsyncOperation)
private delegate void WorkerEventHandler( int numberToCheck, AsyncOperation asyncOp);
Crie uma coleção para gerenciar o tempo de vida de operações assíncronas pendentes. O cliente precisa de uma maneira de rastrear as operações, como elas são executadas e concluídas e esse controle é feito com que o cliente precise passar um símbolo exclusivo ou o ID da tarefa, quando o cliente faz a chamada ao método assíncrono. O PrimeNumberCalculator componente deve manter o controle de cada chamada, associando a ID da tarefa com sua invocação correspondente. Se o cliente passar um ID de tarefa que não é exclusivo, o PrimeNumberCalculator componente deve gerar uma exceção.
O PrimeNumberCalculator componente mantém controle da ID da tarefa usando uma classe de coleção especial chamada um HybridDictionary. Na definição de classe, criar uma HybridDictionary chamado userTokenToLifetime.
Private userStateToLifetime As New HybridDictionary()
private HybridDictionary userStateToLifetime = new HybridDictionary();
A implementação de eventos públicos
Componentes que implementam o padrão assíncrono baseado em evento se comunicar usando eventos de clientes. Esses eventos são invocados no thread correto com a Ajuda do AsyncOperation classe.
Para elevar eventos para os clientes do componente:
Implemente eventos públicos para emissão de relatórios para clientes. Você precisará de um evento por relatar o andamento e outra 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); } }
Implementando o método de conclusão
O delegado de conclusão é o método que invocará o comportamento assíncrono subjacente, segmentação livre quando a operação assíncrona termina a conclusão com êxito, erro ou cancelamento. Esta chamada acontece em um thread arbitrário.
Esse método é onde o ID da tarefa do cliente é removido da coleção interna de tokens de cliente exclusivo. Esse método também termina a vida útil de uma determinada operação assíncrona chamando o PostOperationCompleted método correspondente AsyncOperation. Essa chamada dispara o evento de conclusão no thread que é apropriado para o modelo de aplicativo. Após a PostOperationCompleted método é chamado, esta instância do AsyncOperation não pode mais ser usado, e todas as tentativas subseqüentes para usá-lo lançará uma exceção.
O CompletionMethod assinatura deverá manter o estado de todas as necessário para descrever o resultado da operação assíncrona. Ele mantém o estado para o número que foi testado por essa operação assíncrona em particular, se o número é prime e o valor do seu primeiro divisor se não for um número primo. Ele também mantém o estado descrevendo qualquer exceção que ocorreu, e o AsyncOperation correspondente a esta tarefa específica.
Para concluir uma operação assíncrona:
Implemente o método de conclusão. Ele tem seis parâmetros, ele usa para preencher um CalculatePrimeCompletedEventArgs que é retornado ao cliente por meio do cliente CalculatePrimeCompletedEventHandler. Ele remove o token de ID de tarefa do cliente da coleção interna e termina a vida da operação assíncrona com uma chamada para PostOperationCompleted. O AsyncOperation empacota a chamada do thread ou contexto que é 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. }
Ponto de Verificação
Neste ponto, você pode criar o componente.
Para testar seu componente.
Compile 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é então, você implementou o código assíncrono de suporte 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, and IsPrime. Juntos, BuildPrimeNumberList e IsPrime compõem um algoritmo bastante conhecido, chamado de Sieve de Eratosthenes, que determina se é um número primo encontrando todos os números primos até a raiz quadrada do número de teste. Se nenhum divisors forem encontradas por esse ponto, o número de teste é prime.
Se este componente foram escrito para o máximo de eficiência, seria Lembre-se todos os números primos descobertos por várias chamadas para números de teste diferentes. Ele também deve verificar triviais divisors como 2, 3 e 5. O objetivo deste exemplo é demonstrar como demoradas operações podem ser executadas de forma assíncrona, no entanto, para que essas otimizações são deixadas como um exercício para você.
O CalculateWorker é empacotado em um delegado de método e é chamado de forma assíncrona com uma chamada para BeginInvoke.
Observação
Relatório de progresso é implementado no BuildPrimeNumberList método.Em computadores rápidos, ProgressChanged eventos podem ser gerados em sucessão rápida.O segmento do cliente, no qual esses eventos são gerados, deve ser capaz de lidar com essa situação.O código de interface do usuário pode ser inundadas com mensagens e não é possível acompanhar, resultando em deslocamento de comportamento.Para uma interface de usuário de exemplo que lida com essa situação, consulte Como: Implementar um cliente do padrão assíncrono baseado em evento.
Para executar o cálculo do número primo de forma assíncrona:
Implementar a TaskCanceled método de utilitário. Isso verifica a coleção de tempo de vida da tarefa para a identificação de determinada tarefa e retorna true se a ID 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 ); }
Implemente o método CalculateWorker. Ele usa dois parâmetros: um número para testar e uma 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); }
Implemente BuildPrimeNumberList. Ele usa dois parâmetros: o número para testar e um AsyncOperation. Ele usa o AsyncOperation para relatar o progresso e resultados incrementais. Isso garante que os manipuladores de eventos do cliente são chamados no segmento apropriado ou contexto para o modelo de aplicativo. Quando BuildPrimeNumberList localiza um número primo, ele relata isso como um resultado incremental para o manipulador de eventos do cliente para o ProgressChanged de evento. Isso requer uma classe derivada de ProgressChangedEventArgs, chamado CalculatePrimeProgressChangedEventArgs, que tem uma propriedade adicional chamada LatestPrimeNumber.
O BuildPrimeNumberList também periodicamente o método chama o TaskCanceled método e sai, se o método retorna 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; }
Implemente IsPrime. Ele usa três parâmetros: uma lista dos números primos de conhecidos, o número de teste 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; }
Derive CalculatePrimeProgressChangedEventArgs from ProgressChangedEventArgs. Essa classe é necessário para se reportar resultados incrementais para o manipulador de eventos do cliente para o ProgressChanged de 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; } } }
Ponto de Verificação
Neste ponto, você pode criar o componente.
Para testar seu componente.
Compile o componente.
Tudo o que resta a ser gravados é os métodos para iniciar e cancelar operações assíncronas, CalculatePrimeAsync e CancelAsync.
Implementando o início e os métodos de cancelar
Inicie o método de trabalho em seu próprio segmento, chamando BeginInvoke em que o delegado que encapsula o proprietário. Para gerenciar a vida útil de uma determinada operação assíncrona, você chamar o CreateOperation método o AsyncOperationManager classe de auxiliar. Isso retorna um AsyncOperation, que empacota a chamadas de manipuladores de eventos do cliente para o segmento apropriado ou contexto.
Cancelar uma determinada operação pendente, chamando PostOperationCompleted em seu correspondente AsyncOperation. Aqui termina a operação e todas as chamadas subseqüentes para seu AsyncOperation lançará uma exceção.
Para implementar o início e cancelar a funcionalidade:
Implemente o método CalculatePrimeAsync. Verifique se que o token fornecido pelo cliente (ID de tarefa) é exclusivo em relação a todos os tokens representando atualmente tarefas pendentes. Se o cliente passar em um token não exclusivo, CalculatePrimeAsync gera uma exceção. Caso contrário, o token é adicionado à coleção de ID de 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); }
Implemente o método CancelAsync. Se a taskId parâmetro existe na coleção token, ele será removido. Isso impede que tarefas canceladas não iniciaram a execução. Se a execução da tarefa, o BuildPrimeNumberList método sai quando ele detecta que a ID 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); } } }
Ponto de Verificação
Neste ponto, você pode criar o componente.
Para testar seu componente.
- Compile o componente.
O PrimeNumberCalculator componente agora está concluída e pronta para uso.
Para um cliente de exemplo que usa o PrimeNumberCalculator o componente, consulte Como: Implementar um cliente do padrão assíncrono baseado em evento.
Próximas etapas
Preencha este exemplo escrevendo CalculatePrime, síncrono equivalente a CalculatePrimeAsync método. Isso fará com que o PrimeNumberCalculator componente totalmente compatível com o Event-based Asynchronous padrão.
Você pode melhorar este exemplo mantendo a lista de todos os números primos descoberto por várias chamadas para números de teste diferentes. Com essa abordagem, cada tarefa será beneficiada com o trabalho feito 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 em segundo plano
Como: Implementar um componente que suporta o padrão assíncrono baseado em evento
Conceitos
Event-based Asynchronous Pattern Overview
Outros recursos
Multithreading in Visual Basic
Programação multithread com o padrão assíncrono baseado em evento