Esporta (0) Stampa
Espandi tutto
Il presente articolo è stato tradotto automaticamente. Passare il puntatore sulle frasi nell'articolo per visualizzare il testo originale. Ulteriori informazioni.
Traduzione
Originale

Procedura dettagliata: implementazione di un componente che supporta il modello asincrono basato su eventi

Se la scrittura di una classe implica operazioni che possono causare ritardi notevoli, è possibile assegnare alla classe la funzionalità asincrona implementando Cenni preliminari sul modello asincrono basato su eventi.

In questa procedura dettagliata viene illustrato come creare un componente che implementa il modello asincrono basato su eventi. L'implementazione viene eseguita mediante le classi di supporto dello spazio dei nomi System.ComponentModel. In questo modo è garantito il funzionamento del componente in qualsiasi modello di applicazione, incluse le applicazioni ASP.NET, console e Windows Form. Questo componente può anche essere progettato con un controllo PropertyGrid e con strumenti di progettazione personalizzati.

Nel corso della procedura si disporrà di un'applicazione che consente di calcolare in modo asincrono i numeri primi. L'applicazione in questione contiene un thread dell'interfaccia utente principale e un thread per ogni calcolo eseguito sui numeri primi. Sebbene il test eseguito per verificare se un numero grande è un numero primo possa richiedere una notevole quantità di tempo, il thread dell'interfaccia utente principale non verrà interrotto da questo ritardo e il form sarà reattivo nel corso dei calcoli. Sarà possibile eseguire il numero desiderato di calcoli concorrenti e annullare in modo selettivo i calcoli in sospeso.

Di seguito vengono elencate le attività illustrate nella procedura dettagliata:

  • Creazione del componente

  • Definizione di eventi e delegati asincroni pubblici

  • Definizione di delegati privati

  • Implementazione di eventi pubblici

  • Implementazione del metodo di completamento

  • Implementazione dei metodi di lavoro

  • Implementazione dei metodi di avvio e annullamento

Per copiare il codice nell'argomento corrente come un elenco singolo, vedere Procedura: implementare un componente che supporta il modello asincrono basato su eventi.

Il primo passaggio consiste nella creazione del componente che implementerà il modello asincrono basato su eventi.

Per creare un componente

  • Creare una classe denominata PrimeNumberCalculator che eredita da Component.

Il componente comunica con i client per mezzo degli eventi. L'evento MethodNameCompleted avvisa i client del completamento di un'attività asincrona, mentre l'evento NomeMetodoProgressChanged li informa dello stato di avanzamento di tale attività.

Per definire eventi asincroni per i client del componente:

  1. Importare gli spazi dei nomi System.Threading e System.Collections.Specialized nella parte superiore del file.

    
    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;
    
    
    
  2. Prima della definizione della classe PrimeNumberCalculator , dichiarare i delegati per gli eventi di avanzamento e completamento.

    
    public delegate void ProgressChangedEventHandler(
        ProgressChangedEventArgs e);
    
    public delegate void CalculatePrimeCompletedEventHandler(
        object sender,
        CalculatePrimeCompletedEventArgs e);
    
    
    
  3. Prima della definizione della classe PrimeNumberCalculator , dichiarare gli eventi per la generazione di report sullo stato di avanzamento e sul completamento destinati ai client.

    
    public event ProgressChangedEventHandler ProgressChanged;
    public event CalculatePrimeCompletedEventHandler CalculatePrimeCompleted;
    
    
    
  4. Dopo la definizione della classe PrimeNumberCalculator , derivare la classe CalculatePrimeCompletedEventArgs per la generazione del report sul risultato di ogni calcolo destinato al gestore eventi del client relativo all'evento CalculatePrimeCompleted. Oltre alle proprietà AsyncCompletedEventArgs, questa classe consente al client di determinare il numero sottoposto a test, se si tratta di un numero primo e il valore del primo divisore, se non è numero primo.

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

A questo punto, è possibile compilare il componente.

Per eseguire il test del componente

  • Compilare il componente.

    Verranno visualizzati due messaggi di avviso del compilatore:

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

    Questi messaggi verranno eliminati nella sezione successiva.

Le caratteristiche asincrone del componente PrimeNumberCalculator vengono implementate internamente con un delegato speciale noto come SendOrPostCallback Un SendOrPostCallback che rappresenta un metodo di callback eseguito su un thread ThreadPool. Il metodo di callback deve presentare una firma che accetti un solo parametro di tipo Object. In altre parole, sarà necessario passare lo stato tra i delegati di una classe wrapper. Per ulteriori informazioni, vedere SendOrPostCallback.

Per implementare il comportamento asincrono interno del componente:

  1. Dichiarare e creare i delegati SendOrPostCallback nella classe PrimeNumberCalculator . Creare gli oggetti SendOrPostCallback in un metodo di utilità denominato InitializeDelegates.

    Saranno necessari due delegati, ovvero uno per segnalare lo stato di avanzamento al client e uno per informarlo del completamento dell'operazione.

    
    private SendOrPostCallback onProgressReportDelegate;
    private SendOrPostCallback onCompletedDelegate;
    
    
    ...
    
    
    protected virtual void InitializeDelegates()
    {
        onProgressReportDelegate =
            new SendOrPostCallback(ReportProgress);
        onCompletedDelegate =
            new SendOrPostCallback(CalculateCompleted);
    }
    
    
    
  2. Chiamare il metodo InitializeDelegates nel costruttore del componente.

    
    public PrimeNumberCalculator()
    {   
        InitializeComponent();
    
        InitializeDelegates();
    }
    
    
    
  3. Dichiarare un delegato nella classe PrimeNumberCalculator che gestisce il lavoro effettivo da eseguire in modo asincrono. Questo delegato esegue il wrapping del metodo di lavoro che esegue il test per verificare se un numero è numero primo. Il delegato accetta un parametro AsyncOperation che verrà utilizzato per tenere traccia del ciclo di vita dell'operazione asincrona.

    
    private delegate void WorkerEventHandler(
        int numberToCheck,
        AsyncOperation asyncOp);
    
    
    
  4. Creare una raccolta per la gestione dei cicli di vita delle operazioni asincrone in sospeso. Al client è necessario un sistema per tenere traccia delle operazioni nel corso dell'esecuzione e al completamento e tale sistema consiste nella richiesta di passare un token univoco o un ID attività quando il client effettua la chiamata al metodo asincrono. Il componente PrimeNumberCalculator deve tenere traccia di ogni chiamata associando l'ID attività al relativo richiamo. Se il client passa un ID attività non univoco, il componente PrimeNumberCalculator deve generare un'eccezione.

    Il componente PrimeNumberCalculator tiene traccia dell'ID attività utilizzando una classe di raccolta speciale denominata HybridDictionary. Nella definizione della classe creare un oggetto HybridDictionary denominato userTokenToLifetime.

    
    private HybridDictionary userStateToLifetime = 
        new HybridDictionary();
    
    
    

I componenti che implementano il modello asincrono basato su eventi comunicano con i client mediante gli eventi. Tali eventi vengono richiamati sul thread appropriato con il supporto della classe AsyncOperation.

Per generare eventi per i client del componente:

  • Implementare gli eventi pubblici per la generazione di report destinati ai client. Sono necessari un evento per la generazione di report sullo stato di avanzamento e uno per la generazione di report sul completamento.

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

Il delegato di completamento è il metodo richiamato dal comportamento asincrono di threading Free sottostante quando un'operazione asincrona viene terminata per completamento, errore o annullamento. Questo richiamo si verifica su un thread arbitrario.

Il metodo in questione rappresenta la posizione in cui l'ID attività del client viene rimosso dalla raccolta interna di token client univoci e termina il ciclo di vita di una determinata operazione asincrona mediante la chiamata del metodo PostOperationCompleted sull'oggetto AsyncOperation corrispondente. Questa chiamata genera l'evento di completamento sul thread appropriato per il modello di applicazione. In seguito alla chiamata del metodo PostOperationCompleted, questa istanza di AsyncOperation non potrà essere più utilizzata. Pertanto, tutti i tentativi successivi di utilizzo genereranno un'eccezione.

La firma CompletionMethod deve contenere qualsiasi stato necessario a descrivere il risultato dell'operazione asincrona. Il parametro contiene lo stato relativo al numero sottoposto a test nel corso dell'operazione asincrona, la specifica di numero primo, se applicabile, e il valore del primo divisore, se non corrisponde a un numero primo, nonché lo stato che descrive le eventuali eccezioni generate e l'oggetto AsyncOperation corrispondente all'attività in questione.

Per completare un'operazione asincrona:

  • Implementare il metodo di completamento Accetta sei parametri che utilizza per inserire i dati in un oggetto CalculatePrimeCompletedEventArgs restituito al client tramite l'oggetto CalculatePrimeCompletedEventHandler del client. Tale metodo rimuove l'ID attività del client dalla raccolta interna e termina il ciclo di vita dell'operazione asincrona con una chiamata al metodo PostOperationCompleted. L'oggetto AsyncOperation esegue il marshalling della chiamata al thread o al contesto appropriato per il modello di applicazione.

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

A questo punto, è possibile compilare il componente.

Per eseguire il test del componente

  • Compilare il componente.

    Verrà visualizzato un messaggio di avviso del compilatore:

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

    Questo messaggio verrà eliminato nella sezione successiva.

Finora è stato implementato il codice asincrono di supporto del componente PrimeNumberCalculator. A questo punto, è possibile implementare il codice che esegue le operazioni effettive. Verranno implementati tre metodi: CalculateWorker, BuildPrimeNumberList e IsPrime. Insieme, i metodi BuildPrimeNumberList e IsPrime formano un algoritmo, noto come crivello di Eratostene, che determina se un numero è primo mediante la ricerca di tutti i numeri primi fino alla radice quadrata del numero sottoposto a test. Se non vengono individuati divisori in corrispondenza di tale punto, il numero sottoposto a test è un numero primo.

Se il componente fosse stato scritto per ottenere i massimi risultati, avrebbe tenuto traccia di tutti i numeri primi rilevati dai vari richiami dei diversi numeri sottoposti a test Avrebbe inoltre verificato la presenza di divisori triviali come 2, 3 e 5. Lo scopo di questo esempio è dimostrare come eseguire in modo asincrono operazioni che richiedono molto tempo. Pertanto, questi esempi di ottimizzazione vengono lasciati come esercizio per l'utente.

Il metodo CalculateWorker viene sottoposto a wrapping in un delegato e richiamato in modo asincrono con una chiamata a BeginInvoke.

Nota Nota

La segnalazione dello stato di avanzamento è implementata nel metodo BuildPrimeNumberList. Nei computer veloci gli eventi ProgressChanged possono essere generati in rapida successione. Il thread del client, su cui vengono generati tali eventi, deve essere in grado di gestire la situazione. Il codice dell'interfaccia utente può venire sovraccaricato dai messaggi e non riuscire a tenere il passo, giungendo a un punto di stallo. Per un esempio di interfaccia utente in grado di gestire tale situazione, vedere Procedura: implementare un client del modello asincrono basato su eventi.

Per eseguire in modo asincrono il calcolo dei numeri primi:

  1. Implementare il metodo di utilità TaskCanceled. Viene così verificato se nella raccolta dei cicli di vita delle attività è presente l'ID attività specificato e, in caso negativo, viene restituito true.

    
    // Utility method for determining if a 
    // task has been canceled.
    private bool TaskCanceled(object taskId)
    {
        return( userStateToLifetime[taskId] == null );
    }
    
    
    
  2. Implementare il metodo CalculateWorker. Il metodo accetta due parametri, ovvero il numero da sottoporre a test e un oggetto 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);
    }
    
    
    
  3. Implementare BuildPrimeNumberList Il metodo accetta due parametri, ovvero il numero da sottoporre a test e un oggetto AsyncOperation, e utilizza l'oggetto AsyncOperation per generare report sullo stato di avanzamento e sui risultati incrementali. In questo modo è garantito che i gestori eventi del client vengano chiamati sul thread o sul contesto appropriato al modello di applicazione. Quando BuildPrimeNumberList trova un numero primo, lo inserisce come risultato incrementale in un report destinato al gestore eventi del client per l'evento ProgressChanged. È richiesta una classe derivata da ProgressChangedEventArgs, denominata CalculatePrimeProgressChangedEventArgs, avente una proprietà aggiuntiva LatestPrimeNumber.

    Il metodo BuildPrimeNumberList inoltre chiama periodicamente il metodo TaskCanceled e viene terminato se viene restituito 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;
    }
    
    
    
  4. Implementare IsPrime Tale metodo accetta tre parametri, ovvero un elenco dei numeri primi noti, il numero di cui eseguire il test e un parametro di output per il primo divisore trovato. Partendo dall'elenco di numeri primi, il metodo determina se il numero sottoposto a test ne fa parte.

    
    // 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;
    }
    
    
    
  5. Derivare CalculatePrimeProgressChangedEventArgs da ProgressChangedEventArgs. Questa classe è necessaria per la generazione di report sui risultati incrementali destinati al gestore eventi del client relativo all'evento ProgressChanged e presenta una proprietà aggiuntiva denominata LatestPrimeNumber.

    
    public class CalculatePrimeProgressChangedEventArgs :
            ProgressChangedEventArgs
    {
        private int latestPrimeNumberValue = 1;
    
        public CalculatePrimeProgressChangedEventArgs(
            int latestPrime,
            int progressPercentage,
            object userToken) : base( progressPercentage, userToken )
        {
            this.latestPrimeNumberValue = latestPrime;
        }
    
        public int LatestPrimeNumber
        {
            get
            {
                return latestPrimeNumberValue;
            }
        }
    }
    
    
    

A questo punto, è possibile compilare il componente.

Per eseguire il test del componente

  • Compilare il componente.

    Gli unici elementi ancora da scrivere sono i metodi di avvio e annullamento delle operazioni asincrone, CalculatePrimeAsync e CancelAsync.

Per avviare il metodo di lavoro sul relativo thread è possibile chiamare BeginInvoke sul delegato che lo contiene. Per gestire il ciclo di vita di una determinata operazione asincrona, è possibile chiamare il metodo CreateOperation sulla classe di supporto AsyncOperationManager. Viene restituito un oggetto AsyncOperation che esegue il marshalling delle chiamate sui gestori eventi del client al thread o al contesto appropriato.

Per annullare una determinata operazione in corso è possibile chiamare PostOperationCompleted sull'oggetto AsyncOperation corrispondente. In questo modo l'operazione specifica viene terminata e tutte le chiamate successive al relativo oggetto AsyncOperation generano un'eccezione.

Per implementare la funzionalità di avvio e annullamento:

  1. Implementare il metodo CalculatePrimeAsync. Verificare che il token fornito dal client (ID attività) sia univoco rispetto a tutti i token che rappresentano le attività in sospeso. Se il client passa un token non univoco, CalculatePrimeAsync genera un'eccezione. In caso contrario, il token viene aggiunto alla raccolta di ID attività.

    
    // 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);
    }
    
    
    
  2. Implementare il metodo CancelAsync. Se nella raccolta dei token è presente il parametro taskId, verrà rimosso. In questo modo viene impedita l'esecuzione delle attività annullate non ancora avviate. Se l'attività è in esecuzione, il metodo BuildPrimeNumberList viene terminato quando rileva che l'ID attività è stato rimosso dalla raccolta dei cicli di vita.

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

A questo punto, è possibile compilare il componente.

Per eseguire il test del componente

  • Compilare il componente.

A questo punto, il componente PrimeNumberCalculator è completo e può essere utilizzato.

Per un client di esempio che utilizzi il componente PrimeNumberCalculator, vedere Procedura: implementare un client del modello asincrono basato su eventi.

Per completare l'esempio è possibile scrivere CalculatePrime, l'equivalente sincrono del metodo CalculatePrimeAsync. In questo modo il componente PrimeNumberCalculator verrà reso completamente conforme al modello asincrono basato su eventi.

Per migliorare l'esempio è possibile conservare l'elenco di tutti i numeri primi rilevati durante i vari richiami dei diversi numeri sottoposti a test. Con questo approccio ogni attività potrà usufruire dei vantaggi derivanti dalle attività già eseguite. Proteggere l'elenco con le aree lock in modo da serializzarne l'accesso da parte dei diversi thread.

Per migliorare ulteriormente l'esempio è possibile eseguire il test di divisori triviali come 2, 3 e 5.

Aggiunte alla community

AGGIUNGI
Microsoft sta conducendo un sondaggio in linea per comprendere l'opinione degli utenti in merito al sito Web di MSDN. Se si sceglie di partecipare, quando si lascia il sito Web di MSDN verrà visualizzato il sondaggio in linea.

Si desidera partecipare?
Mostra:
© 2014 Microsoft