Utilizzo del modello asincrono basato su attivitàConsuming the Task-based Asynchronous Pattern

Quando si usa il modello asincrono basato su attività (TAP, Task-based Asynchronous Pattern) per lavorare con operazioni asincrone, è possibile usare i callback per ottenere un'attesa non bloccante.When you use the Task-based Asynchronous Pattern (TAP) to work with asynchronous operations, you can use callbacks to achieve waiting without blocking. Per le attività, è possibile ottenere questo risultato tramite metodi come Task.ContinueWith.For tasks, this is achieved through methods such as Task.ContinueWith. Il supporto asincrono basato sul linguaggio nasconde i callback consentendo alle operazioni asincrone di essere in attesa all'interno di un normale flusso di controllo e il codice generato dal compilatore fornisce lo stesso livello di supporto delle API.Language-based asynchronous support hides callbacks by allowing asynchronous operations to be awaited within normal control flow, and compiler-generated code provides this same API-level support.

Sospendere l'esecuzione con AwaitSuspending Execution with Await

A partire da .NET Framework 4.5.NET Framework 4.5 è possibile usare la parola chiave await in C# e l'operatore Await in Visual Basic per attendere in modo asincrono gli oggetti Task e Task<TResult>.Starting with the .NET Framework 4.5.NET Framework 4.5, you can use the await keyword in C# and the Await Operator in Visual Basic to asynchronously await Task and Task<TResult> objects. Quando si è in attesa di Task, l'espressione await è di tipo void.When you're awaiting a Task, the await expression is of type void. Quando si è in attesa di Task<TResult>, l'espressione await è di tipo TResult.When you're awaiting a Task<TResult>, the await expression is of type TResult. Un'espressione await deve essere presente nel corpo di un metodo asincrono.An await expression must occur inside the body of an asynchronous method. Per altre informazioni sul supporto dei linguaggi C# e Visual Basic in .NET Framework 4.5.NET Framework 4.5, vedere le specifiche dei linguaggi C# e Visual Basic.For more information about C# and Visual Basic language support in the .NET Framework 4.5.NET Framework 4.5, see the C# and Visual Basic language specifications.

Dietro le quinte, la funzionalità await installa un callback per l'attività tramite una continuazione.Under the covers, the await functionality installs a callback on the task by using a continuation. Questo callback riprende il metodo asincrono in corrispondenza del punto di sospensione.This callback resumes the asynchronous method at the point of suspension. Quando il metodo asincrono viene ripreso, se l'operazione attesa è stata completata correttamente ed era Task<TResult>, viene restituito il corrispondente elemento TResult.When the asynchronous method is resumed, if the awaited operation completed successfully and was a Task<TResult>, its TResult is returned. Se l'elemento Task o Task<TResult> atteso è terminato con lo stato Canceled, viene generata un'eccezione OperationCanceledException.If the Task or Task<TResult> that was awaited ended in the Canceled state, an OperationCanceledException exception is thrown. Se l'elemento Task o Task<TResult> atteso è terminato con lo stato Faulted, viene generata l'eccezione che ha causato l'errore.If the Task or Task<TResult> that was awaited ended in the Faulted state, the exception that caused it to fault is thrown. Un Task può fallire a causa di più eccezioni, ma solo una di queste eccezioni viene propagata.A Task can fault as a result of multiple exceptions, but only one of these exceptions is propagated. La proprietà Task.Exception restituisce tuttavia un'eccezione AggregateException contenente tutti gli errori.However, the Task.Exception property returns an AggregateException exception that contains all the errors.

Se un contesto di sincronizzazione (oggetto SynchronizationContext) è associato al thread che stava eseguendo il metodo asincrono durante la sospensione (ad esempio, se la proprietà SynchronizationContext.Current non è null), il metodo asincrono riprende nello stesso contesto di sincronizzazione usando il metodo Post del contesto.If a synchronization context (SynchronizationContext object) is associated with the thread that was executing the asynchronous method at the time of suspension (for example, if the SynchronizationContext.Current property is not null), the asynchronous method resumes on that same synchronization context by using the context’s Post method. In caso contrario, si basa sull'utilità di pianificazione (oggetto TaskScheduler) che è corrente al momento della sospensione.Otherwise, it relies on the task scheduler (TaskScheduler object) that was current at the time of suspension. Si tratta in genere dell'utilità di pianificazione predefinita (TaskScheduler.Default), che specifica come destinazione il pool di thread.Typically, this is the default task scheduler (TaskScheduler.Default), which targets the thread pool. L'utilità di pianificazione determina se l'operazione asincrona in attesa deve riprendere al momento del completamento o se la ripresa deve essere pianificata.This task scheduler determines whether the awaited asynchronous operation should resume where it completed or whether the resumption should be scheduled. L'utilità di pianificazione predefinita consente in genere l'esecuzione della continuazione nel thread in cui l'operazione attesa è stata completata.The default scheduler typically allows the continuation to run on the thread that the awaited operation completed.

Quando un metodo asincrono viene chiamato, esegue in modo sincrono il corpo della funzione fino alla prima espressione await su un'istanza awaitable che non è ancora terminata, a quel punto la chiamata restituisce il controllo al chiamante.When an asynchronous method is called, it synchronously executes the body of the function up until the first await expression on an awaitable instance that has not yet completed, at which point the invocation returns to the caller. Se il metodo asincrono non restituisce void, viene restituito un oggetto Task o Task<TResult> per rappresentare il calcolo in corso.If the asynchronous method does not return void, a Task or Task<TResult> object is returned to represent the ongoing computation. In un metodo asincrono non void, se viene rilevata un'istruzione return o viene raggiunta la fine del corpo del metodo, l'attività termina con lo stato finale RanToCompletion.In a non-void asynchronous method, if a return statement is encountered or the end of the method body is reached, the task is completed in the RanToCompletion final state. Se un'eccezione non gestita fa in modo che il controllo lasci il corpo del metodo asincrono, l'attività termina con lo stato Faulted.If an unhandled exception causes control to leave the body of the asynchronous method, the task ends in the Faulted state. Se tale eccezione è OperationCanceledException, l'attività termina invece con lo stato Canceled.If that exception is an OperationCanceledException, the task instead ends in the Canceled state. Viene in questo modo pubblicato il risultato o l'eccezione.In this manner, the result or exception is eventually published.

Vi sono diverse varianti importanti di questo comportamento.There are several important variations of this behavior. Per motivi di prestazioni, se un processo viene completato prima che sia atteso, il controllo non viene prodotto e la funzione prosegue nell'esecuzione.For performance reasons, if a task has already completed by the time the task is awaited, control is not yielded, and the function continues to execute. Non è inoltre sempre desiderabile tornare al contesto originale e questo comportamento può essere modificato. La sezione seguente descrive questa situazione nei dettagli.Additionally, returning to the original context isn't always the desired behavior and can be changed; this is described in more detail in the next section.

Configurazione della sospensione e della ripresa con Yield e ConfigureAwaitConfiguring Suspension and Resumption with Yield and ConfigureAwait

Esistono diversi metodi che forniscono maggiore controllo sull'esecuzione di un metodo asincrono.Several methods provide more control over an asynchronous method’s execution. È ad esempio possibile usare il metodo Task.Yield per introdurre un punto di sospensione nel metodo asincrono:For example, you can use the Task.Yield method to introduce a yield point into the asynchronous method:

public class Task : …  
{  
    public static YieldAwaitable Yield();  
    …  
}  

Questa operazione equivale a eseguire un postback asincrono o a ritornare in modo asincrono al contesto corrente tramite programmazione.This is equivalent to asynchronously posting or scheduling back to the current context.

Task.Run(async delegate  
{  
    for(int i=0; i<1000000; i++)  
    {  
        await Task.Yield(); // fork the continuation into a separate work item  
        ...  
    }  
});  

È anche possibile usare il metodo Task.ConfigureAwait per controllare meglio la sospensione e la ripresa in un metodo asincrono.You can also use the Task.ConfigureAwait method for better control over suspension and resumption in an asynchronous method. Come accennato in precedenza, per impostazione predefinita il contesto corrente viene acquisito nel momento in cui un metodo asincrono viene sospeso e questo contesto acquisito viene utilizzato per richiamare la continuazione del metodo asincrono al momento della ripresa.As mentioned previously, by default, the current context is captured at the time an asynchronous method is suspended, and that captured context is used to invoke the asynchronous method’s continuation upon resumption. In molti casi questo è il comportamento desiderato.In many cases, this is the exact behavior you want. In altri casi non è necessario preoccuparsi del contesto di continuazione ed è possibile ottenere migliori prestazioni evitando tali postback al contesto originale.In other cases, you may not care about the continuation context, and you can achieve better performance by avoiding such posts back to the original context. Usare a tale scopo il metodo Task.ConfigureAwait per indicare all'operazione await di non acquisire e riprendere il contesto, ma di continuare l'esecuzione quando l'operazione asincrona di cui si era in attesa viene completata:To enable this, use the Task.ConfigureAwait method to inform the await operation not to capture and resume on the context, but to continue execution wherever the asynchronous operation that was being awaited completed:

await someTask.ConfigureAwait(continueOnCapturedContext:false);  

Annullamento di un'operazione asincronaCanceling an Asynchronous Operation

A partire da .NET Framework 4.NET Framework 4, i metodi TAP che supportano l'annullamento forniscono almeno un overload che accetta un token di annullamento (oggetto CancellationToken).Starting with the .NET Framework 4.NET Framework 4, TAP methods that support cancellation provide at least one overload that accepts a cancellation token (CancellationToken object).

Un token di annullamento viene creato tramite un'origine di token di annullamento (oggetto CancellationTokenSource).A cancellation token is created through a cancellation token source (CancellationTokenSource object). La proprietà Token dell'origine restituisce il token di annullamento che verrà segnalato quando il metodo Cancel dell'origine verrà chiamato.The source’s Token property returns the cancellation token that will be signaled when the source’s Cancel method is called. Ad esempio, per scaricare una singola pagina Web e poter annullare l'operazione, si crea un oggetto CancellationTokenSource, si passa il token al metodo TAP e quindi, quando si è pronti per annullare l'operazione, si chiama il metodo Cancel dell'origine:For example, if you want to download a single webpage and you want to be able to cancel the operation, you create a CancellationTokenSource object, pass its token to the TAP method, and then call the source’s Cancel method when you're ready to cancel the operation:

var cts = new CancellationTokenSource();  
string result = await DownloadStringAsync(url, cts.Token);  
… // at some point later, potentially on another thread  
cts.Cancel();  

Per annullare più chiamate asincrone, è possibile passare lo stesso token per tutte le chiamate:To cancel multiple asynchronous invocations, you can pass the same token to all invocations:

var cts = new CancellationTokenSource();  
    IList<string> results = await Task.WhenAll(from url in urls select DownloadStringAsync(url, cts.Token));  
    // at some point later, potentially on another thread  
    …  
    cts.Cancel();  

È in alternativa possibile passare lo stesso token a un sottoinsieme selettivo di operazioni:Or, you can pass the same token to a selective subset of operations:

var cts = new CancellationTokenSource();  
    byte [] data = await DownloadDataAsync(url, cts.Token);  
    await SaveToDiskAsync(outputPath, data, CancellationToken.None);  
    … // at some point later, potentially on another thread  
    cts.Cancel();  

Le richieste di annullamento possono essere avviate da qualsiasi thread.Cancellation requests may be initiated from any thread.

È possibile passare il valore di CancellationToken.None a ogni metodo che accetta un token di annullamento per indicare che non verrà mai richiesto l'annullamento.You can pass the CancellationToken.None value to any method that accepts a cancellation token to indicate that cancellation will never be requested. In tal modo la proprietà CancellationToken.CanBeCanceled restituisce false e il metodo chiamato può essere ottimizzato di conseguenza.This causes the CancellationToken.CanBeCanceled property to return false, and the called method can optimize accordingly. È possibile, a scopo di test, passare un token di annullamento già annullato di cui si è creata un'istanza usando il costruttore che accetta un valore booleano per indicare se il token deve essere avviato in uno stato already-canceled (già cancellato) o not-cancelable (non cancellabile).For testing purposes, you can also pass in a pre-canceled cancellation token that is instantiated by using the constructor that accepts a Boolean value to indicate whether the token should start in an already-canceled or not-cancelable state.

Questo approccio all'annullamento presenta diversi vantaggi:This approach to cancellation has several advantages:

  • È possibile passare lo stesso token di annullamento a un numero qualsiasi di operazioni asincrone e sincrone.You can pass the same cancellation token to any number of asynchronous and synchronous operations.

  • La stessa richiesta di annullamento può essere estesa a un numero qualsiasi di listener.The same cancellation request may be proliferated to any number of listeners.

  • Lo sviluppatore dell'API asincrona ha il controllo totale sull'eventuale richiesta di annullamento e sull'eventuale esecuzione.The developer of the asynchronous API is in complete control of whether cancellation may be requested and when it may take effect.

  • Il codice che usa l'API può determinare in modo selettivo a quali chiamate asincrone verranno propagate le richieste di annullamento.The code that consumes the API may selectively determine the asynchronous invocations that cancellation requests will be propagated to.

Monitoraggio dello statoMonitoring Progress

Alcuni metodi asincroni espongono lo stato di avanzamento tramite un'interfaccia dello stato di avanzamento passata all'interno del metodo asincrono.Some asynchronous methods expose progress through a progress interface passed into the asynchronous method. Si consideri ad esempio una funzione che scarica in modo asincrono una stringa di testo e, durante il processo, genera gli aggiornamenti dello stato di avanzamento che include la percentuale di download completata fino a quel momento.For example, consider a function which asynchronously downloads a string of text, and along the way raises progress updates that include the percentage of the download that has completed thus far. Tale metodo può essere usato in un'applicazione Windows Presentation Foundation (WPF) come segue:Such a method could be consumed in a Windows Presentation Foundation (WPF) application as follows:

private async void btnDownload_Click(object sender, RoutedEventArgs e)    
{  
    btnDownload.IsEnabled = false;  
    try  
    {  
        txtResult.Text = await DownloadStringAsync(txtUrl.Text,   
            new Progress<int>(p => pbDownloadProgress.Value = p));  
    }  
    finally { btnDownload.IsEnabled = true; }  
}  

Utilizzo di combinatori incorporati basati su attivitàUsing the Built-in Task-based Combinators

Lo spazio dei nomi System.Threading.Tasks include diversi metodi per la composizione e l'uso delle attività.The System.Threading.Tasks namespace includes several methods for composing and working with tasks.

Task.RunTask.Run

La classe Task include diversi metodi Run che consentono di eseguire facilmente l'offload del lavoro come Task o Task<TResult> nel pool di thread, ad esempio:The Task class includes several Run methods that let you easily offload work as a Task or Task<TResult> to the thread pool, for example:

public async void button1_Click(object sender, EventArgs e)  
{  
    textBox1.Text = await Task.Run(() =>  
    {  
        // … do compute-bound work here  
        return answer;  
    });  
}  

Alcuni di questi metodi Run, ad esempio l'overload Task.Run(Func<Task>), esistono come abbreviazione per il metodo TaskFactory.StartNew.Some of these Run methods, such as the Task.Run(Func<Task>) overload, exist as shorthand for the TaskFactory.StartNew method. Altri overload, come Task.Run(Func<Task>), consentono di usare await nel lavoro di cui è stato eseguito l'offload, ad esempio:Other overloads, such as Task.Run(Func<Task>), enable you to use await within the offloaded work, for example:

public async void button1_Click(object sender, EventArgs e)  
{  
    pictureBox1.Image = await Task.Run(async() =>  
    {  
        using(Bitmap bmp1 = await DownloadFirstImageAsync())  
        using(Bitmap bmp2 = await DownloadSecondImageAsync())  
        return Mashup(bmp1, bmp2);  
    });  
}  

Tali overload sono logicamente equivalenti all'uso del metodo TaskFactory.StartNew insieme al metodo di estensione Unwrap in Task Parallel Library.Such overloads are logically equivalent to using the TaskFactory.StartNew method in conjunction with the Unwrap extension method in the Task Parallel Library.

Task.FromResultTask.FromResult

Usare il metodo FromResult negli scenari in cui i dati possono essere già disponibili e devono essere solo restituiti da un metodo che restituisce attività del tipo Task<TResult>:Use the FromResult method in scenarios where data may already be available and just needs to be returned from a task-returning method lifted into a Task<TResult>:

public Task<int> GetValueAsync(string key)  
{  
    int cachedValue;  
    return TryGetCachedValue(out cachedValue) ?  
        Task.FromResult(cachedValue) :  
        GetValueAsyncInternal();  
}  

private async Task<int> GetValueAsyncInternal(string key)  
{  
    …  
}  

Task.WhenAllTask.WhenAll

Usare il metodo WhenAll per attendere in modo asincrono più operazioni asincrone rappresentate come attività.Use the WhenAll method to asynchronously wait on multiple asynchronous operations that are represented as tasks. Il metodo dispone di più overload che supportano un set di attività non generiche o un set non uniforme di attività generiche (ad esempio attendere in modo asincrono più operazioni che restituiscono void, o attendere in modo asincrono più metodi che restituiscono dei valori, dove ogni valore può essere di tipo diverso) e supportano un set uniforme di attività generiche (ad esempio attendere in modo asincrono più metodi che restituiscono TResult).The method has multiple overloads that support a set of non-generic tasks or a non-uniform set of generic tasks (for example, asynchronously waiting for multiple void-returning operations, or asynchronously waiting for multiple value-returning methods where each value may have a different type) and to support a uniform set of generic tasks (such as asynchronously waiting for multiple TResult-returning methods).

Si supponga di voler inviare messaggi di posta elettronica a numerosi clienti.Let's say you want to send email messages to several customers. È possibile sovrapporre l'invio dei messaggi in modo da non dover attendere che un messaggio termini prima di inviare il successivo.You can overlap sending the messages so you're not waiting for one message to complete before sending the next. È inoltre possibile scoprire quando le operazioni di invio sono state completate e se si sono verificati degli errori:You can also find out when the send operations have completed and whether any errors have occurred:

IEnumerable<Task> asyncOps = from addr in addrs select SendMailAsync(addr);  
await Task.WhenAll(asyncOps);  

Questo codice non gestisce in modo esplicito le eccezioni che possono verificarsi, ma consente la propagazione delle eccezioni da await sull'attività risultante da WhenAll.This code doesn't explicitly handle exceptions that may occur, but lets exceptions propagate out of the await on the resulting task from WhenAll. Per gestire le eccezioni, è possibile usare codice simile al seguente:To handle the exceptions, you can use code such as the following:

IEnumerable<Task> asyncOps = from addr in addrs select SendMailAsync(addr);  
try  
{  
    await Task.WhenAll(asyncOps);  
}  
catch(Exception exc)  
{  
    ...  
}  

In questo caso, se un'operazione asincrona non riesce, tutte le eccezioni verranno consolidate in un'eccezione AggregateException, archiviata nell'attività Task restituita dal metodo WhenAll.In this case, if any asynchronous operation fails, all the exceptions will be consolidated in an AggregateException exception, which is stored in the Task that is returned from the WhenAll method. Solo una di queste eccezioni viene tuttavia propagata dalla parola chiave await.However, only one of those exceptions is propagated by the await keyword. Se si desidera esaminare tutte le eccezioni, è possibile riscrivere il codice precedente come segue:If you want to examine all the exceptions, you can rewrite the previous code as follows:

Task [] asyncOps = (from addr in addrs select SendMailAsync(addr)).ToArray();  
try  
{  
    await Task.WhenAll(asyncOps);  
}  
catch(Exception exc)  
{  
    foreach(Task faulted in asyncOps.Where(t => t.IsFaulted))  
    {  
        … // work with faulted and faulted.Exception  
    }  
}  

Si consideri un esempio in cui si scaricano più file dal Web in modo asincrono.Let's consider an example of downloading multiple files from the web asynchronously. In questo caso tutte le operazioni asincrone hanno tipi di risultato omogenei ed è facile accedere ai risultati:In this case, all the asynchronous operations have homogeneous result types, and it's easy to access the results:

string [] pages = await Task.WhenAll(  
    from url in urls select DownloadStringAsync(url));  

È possibile applicare le stesse tecniche di gestione delle eccezioni illustrate nello scenario di restituzione di void precedente:You can use the same exception-handling techniques we discussed in the previous void-returning scenario:

Task [] asyncOps =   
    (from url in urls select DownloadStringAsync(url)).ToArray();  
try  
{  
    string [] pages = await Task.WhenAll(asyncOps);  
    ...  
}  
catch(Exception exc)  
{  
    foreach(Task<string> faulted in asyncOps.Where(t => t.IsFaulted))  
    {  
        … // work with faulted and faulted.Exception  
    }  
}  

Task.WhenAnyTask.WhenAny

È possibile usare il metodo WhenAny per attendere in modo asincrono più operazioni asincrone rappresentate come attività da completare.You can use the WhenAny method to asynchronously wait for just one of multiple asynchronous operations represented as tasks to complete. Questo metodo viene usato principalmente in quattro casi:This method serves four primary use cases:

  • Ridondanza: esecuzione ripetuta di un'operazione e selezione di quella che viene terminata per prima (ad esempio contatto di più servizi Web di quotazioni di borsa tramite cui verrà generato un solo risultato e selezione di quello che viene completato più velocemente).Redundancy: Performing an operation multiple times and selecting the one that completes first (for example, contacting multiple stock quote web services that will produce a single result and selecting the one that completes the fastest).

  • Interfoliazione: avvio di più operazioni e attesa del completamento di tutte, ma elaborazione al termine delle operazioni in questione.Interleaving: Launching multiple operations and waiting for all of them to complete, but processing them as they complete.

  • Limitazione: consentire l'avvio di operazioni aggiuntive al completamento delle altre.Throttling: Allowing additional operations to begin as others complete. Questa è un'estensione dello scenario di interfoliazione.This is an extension of the interleaving scenario.

  • Bailout iniziale: ad esempio, un'operazione rappresentata dall'attività t1 può essere raggruppata in un'attività WhenAny con un'altra attività t2 ed è possibile attendere l'attività WhenAny.Early bailout: For example, an operation represented by task t1 can be grouped in a WhenAny task with another task t2, and you can wait on the WhenAny task. L'attività t2 potrebbe rappresentare un timeout, un annullamento o un altro segnale che fa sì che l'attività WhenAny termini prima di t1.Task t2 could represent a time-out, or cancellation, or some other signal that causes the WhenAny task to complete before t1 completes.

RidondanzaRedundancy

Si consideri il caso in cui si desidera decidere se comprare o meno dei titoli.Consider a case where you want to make a decision about whether to buy a stock. Esistono numerosi servizi Web attendibili che consigliano su azioni, ma a seconda del traffico giornaliero, ogni servizio può risultare lento in determinati momenti.There are several stock recommendation web services that you trust, but depending on daily load, each service can end up being slow at different times. È possibile usare il metodo WhenAny per ricevere una notifica quando un'operazione termina:You can use the WhenAny method to receive a notification when any operation completes:

var recommendations = new List<Task<bool>>()   
{   
    GetBuyRecommendation1Async(symbol),   
    GetBuyRecommendation2Async(symbol),  
    GetBuyRecommendation3Async(symbol)  
};  
Task<bool> recommendation = await Task.WhenAny(recommendations);  
if (await recommendation) BuyStock(symbol);  

Diversamente da WhenAll, che restituisce i risultati da cui è stato rimosso il wrapping di tutte le attività completate correttamente, WhenAny restituisce l'attività completata.Unlike WhenAll, which returns the unwrapped results of all tasks that completed successfully, WhenAny returns the task that completed. Se un'attività ha esito negativo, è importante sottolineare che ha avuto esito negativo e se l'attività ha esito positivo, è importante sapere a quale attività è associato il valore restituito.If a task fails, it’s important to know that it failed, and if a task succeeds, it’s important to know which task the return value is associated with. Pertanto, è necessario accedere al risultato dell'attività restituita o attenderlo ulteriormente, come mostrato nell'esempio riportato di seguito.Therefore, you need to access the result of the returned task, or further await it, as this example shows.

Come per WhenAll, è necessario poter adattare le eccezioni.As with WhenAll, you have to be able to accommodate exceptions. Poiché l'attività terminata viene restituita, è possibile attendere l'attività restituita per propagare gli errori ed eseguire try/catch in modo appropriato, ad esempio:Because you receive the completed task back, you can await the returned task to have errors propagated, and try/catch them appropriately; for example:

Task<bool> [] recommendations = …;  
while(recommendations.Count > 0)  
{   
    Task<bool> recommendation = await Task.WhenAny(recommendations);      
    try  
    {  
        if (await recommendation) BuyStock(symbol);  
        break;  
    }  
    catch(WebException exc)  
    {  
        recommendations.Remove(recommendation);  
    }  
}  

Anche se il primo processo viene completato correttamente, le attività successive possono dare esito negativo.Additionally, even if a first task completes successfully, subsequent tasks may fail. A questo punto, sono disponibili diverse opzioni per la gestione delle eccezioni: è possibile attendere il completamento di tutte le attività avviate e, in questo caso, è possibile utilizzare il metodo WhenAll oppure è possibile decidere che tutte le eccezioni sono importanti e devono essere registrate.At this point, you have several options for dealing with exceptions: You can wait until all the launched tasks have completed, in which case you can use the WhenAll method, or you can decide that all exceptions are important and must be logged. È possibile a tale scopo usare le continuazioni per ricevere una notifica quando le attività terminano in modo asincrono:For this, you can use continuations to receive a notification when tasks have completed asynchronously:

foreach(Task recommendation in recommendations)  
{  
    var ignored = recommendation.ContinueWith(  
        t => { if (t.IsFaulted) Log(t.Exception); });  
}  

oppure:or:

foreach(Task recommendation in recommendations)  
{  
    var ignored = recommendation.ContinueWith(  
        t => Log(t.Exception), TaskContinuationOptions.OnlyOnFaulted);  
}  

o anche:or even:

private static async void LogCompletionIfFailed(IEnumerable<Task> tasks)  
{  
    foreach(var task in tasks)  
    {  
        try { await task; }  
        catch(Exception exc) { Log(exc); }  
    }  
}  
…  
LogCompletionIfFailed(recommendations);  

È possibile, infine, annullare tutte le operazioni rimanenti:Finally, you may want to cancel all the remaining operations:

var cts = new CancellationTokenSource();  
var recommendations = new List<Task<bool>>()   
{   
    GetBuyRecommendation1Async(symbol, cts.Token),   
    GetBuyRecommendation2Async(symbol, cts.Token),  
    GetBuyRecommendation3Async(symbol, cts.Token)  
};  

Task<bool> recommendation = await Task.WhenAny(recommendations);  
cts.Cancel();  
if (await recommendation) BuyStock(symbol);  

InterfoliazioneInterleaving

Si consideri il caso in cui si scarichino delle immagini dal Web e ogni immagine venga elaborata, ad esempio aggiungendo l'immagine a un controllo dell'interfaccia utente.Consider a case where you're downloading images from the web and processing each image (for example, adding the image to a UI control). È necessario eseguire l'elaborazione sequenzialmente nell'interfaccia utente del thread, ma si desidera scaricare quante più immagini possibili contemporaneamente.You have to do the processing sequentially on the UI thread, but you want to download the images as concurrently as possible. Non si desidera, tra l'altro, aspettare che le immagini siano tutte scaricate prima di aggiungerle all'interfaccia utente, ma si desidera aggiungerle man mano che vengono scaricate:Also, you don’t want to hold up adding the images to the UI until they’re all downloaded—you want to add them as they complete:

List<Task<Bitmap>> imageTasks =   
    (from imageUrl in urls select GetBitmapAsync(imageUrl)).ToList();  
while(imageTasks.Count > 0)  
{  
    try  
    {  
        Task<Bitmap> imageTask = await Task.WhenAny(imageTasks);  
        imageTasks.Remove(imageTask);  

        Bitmap image = await imageTask;  
        panel.AddImage(image);  
    }  
    catch{}  
}  

È anche possibile applicare l'interfoliazione a uno scenario che include un'elaborazione complessa a livello di calcolo nella classe ThreadPool delle immagini scaricate, ad esempio:You can also apply interleaving to a scenario that involves computationally intensive processing on the ThreadPool of the downloaded images; for example:

List<Task<Bitmap>> imageTasks =   
    (from imageUrl in urls select GetBitmapAsync(imageUrl)  
         .ContinueWith(t => ConvertImage(t.Result)).ToList();  
while(imageTasks.Count > 0)  
{  
    try  
    {  
        Task<Bitmap> imageTask = await Task.WhenAny(imageTasks);  
        imageTasks.Remove(imageTask);  

        Bitmap image = await imageTask;  
        panel.AddImage(image);  
    }  
    catch{}  
}  

LimitazioneThrottling

Si consideri l'esempio dell'interfoliazione, con la differenza che l'utente sta scaricando un numero così elevato di immagini che i download devono essere limitati. Si desidera, ad esempio, che solo uno specifico numero di download sia realizzato contemporaneamente.Consider the interleaving example, except that the user is downloading so many images that the downloads have to be throttled; for example, you want only a specific number of downloads to happen concurrently. È possibile a tale scopo avviare un sottoinsieme di operazioni asincrone.To achieve this, you can start a subset of the asynchronous operations. Mentre le operazioni terminano, è possibile avviare operazioni aggiuntive che prendano il loro posto:As operations complete, you can start additional operations to take their place:

const int CONCURRENCY_LEVEL = 15;  
Uri [] urls = …;  
int nextIndex = 0;  
var imageTasks = new List<Task<Bitmap>>();  
while(nextIndex < CONCURRENCY_LEVEL && nextIndex < urls.Length)  
{  
    imageTasks.Add(GetBitmapAsync(urls[nextIndex]));  
    nextIndex++;  
}  

while(imageTasks.Count > 0)  
{  
    try  
    {  
        Task<Bitmap> imageTask = await Task.WhenAny(imageTasks);  
        imageTasks.Remove(imageTask);  

        Bitmap image = await imageTask;  
        panel.AddImage(image);  
    }  
    catch(Exception exc) { Log(exc); }  

    if (nextIndex < urls.Length)  
    {  
        imageTasks.Add(GetBitmapAsync(urls[nextIndex]));  
        nextIndex++;  
    }  
}  

Bailout inizialeEarly Bailout

Si consideri di essere in attesa in modo asincrono che un'operazione termini e contemporaneamente di rispondere alla richiesta di interruzione da parte di un utente (ad esempio l'utente ha fatto clic su un pulsante di annullamento).Consider that you're waiting asynchronously for an operation to complete while simultaneously responding to a user’s cancellation request (for example, the user clicked a cancel button). Il codice seguente illustra questo scenario:The following code illustrates this scenario:

private CancellationTokenSource m_cts;   

public void btnCancel_Click(object sender, EventArgs e)  
{  
    if (m_cts != null) m_cts.Cancel();  
}  

public async void btnRun_Click(object sender, EventArgs e)  
{  
    m_cts = new CancellationTokenSource();  
    btnRun.Enabled = false;  
    try  
    {  
        Task<Bitmap> imageDownload = GetBitmapAsync(txtUrl.Text);   
        await UntilCompletionOrCancellation(imageDownload, m_cts.Token);  
        if (imageDownload.IsCompleted)  
        {  
            Bitmap image = await imageDownload;  
            panel.AddImage(image);  
        }  
        else imageDownload.ContinueWith(t => Log(t));  
    }  
    finally { btnRun.Enabled = true; }  
}  

private static async Task UntilCompletionOrCancellation(  
    Task asyncOp, CancellationToken ct)  
{  
    var tcs = new TaskCompletionSource<bool>();  
    using(ct.Register(() => tcs.TrySetResult(true)))  
        await Task.WhenAny(asyncOp, tcs.Task);  
    return asyncOp;  
}  

Questa implementazione abilita nuovamente l'interfaccia utente non appena si decide di annullare l'operazione, ma non annulla le operazioni asincrone sottostanti.This implementation re-enables the user interface as soon as you decide to bail out, but doesn't cancel the underlying asynchronous operations. Un'alternativa consiste nell'annullare le operazioni in sospeso quando si decide di interrompere, ma di non ristabilire l'interfaccia utente finché le operazioni non siano terminate, probabilmente perché a causa della richiesta dell'interruzione, sono terminate prima:Another alternative would be to cancel the pending operations when you decide to bail out, but not reestablish the user interface until the operations actually complete, potentially due to ending early due to the cancellation request:

private CancellationTokenSource m_cts;  

public async void btnRun_Click(object sender, EventArgs e)  
{  
    m_cts = new CancellationTokenSource();  

    btnRun.Enabled = false;  
    try  
    {  
        Task<Bitmap> imageDownload = GetBitmapAsync(txtUrl.Text, m_cts.Token);   
        await UntilCompletionOrCancellation(imageDownload, m_cts.Token);  
        Bitmap image = await imageDownload;  
        panel.AddImage(image);  
    }  
    catch(OperationCanceledException) {}  
    finally { btnRun.Enabled = true; }  
}  

Un altro esempio di bailout iniziale prevede l'uso del WhenAny metodo insieme al metodo Delay, come illustrato nella sezione successiva.Another example of early bailout involves using the WhenAny method in conjunction with the Delay method, as discussed in the next section.

Task.DelayTask.Delay

È possibile usare il metodo Task.Delay per introdurre pause nell'esecuzione di un metodo asincrono.You can use the Task.Delay method to introduce pauses into an asynchronous method’s execution. Ciò è utile per molti tipi di funzionalità, incluse la compilazione dei cicli di polling e il posticipo della gestione di input utente per un periodo di tempo predeterminato.This is useful for many kinds of functionality, including building polling loops and delaying the handling of user input for a predetermined period of time. Il metodo Task.Delay può anche essere utile in combinazione con Task.WhenAny per implementare i timeout sulle attese.The Task.Delay method can also be useful in combination with Task.WhenAny for implementing time-outs on awaits.

Se un'attività che fa parte di una operazione asincrona più ampia, ad esempio un servizio web ASP.NET, impiega troppo tempo per terminare, potrebbe soffrirne l'operazione globale, specialmente se non termina mai.If a task that’s part of a larger asynchronous operation (for example, an ASP.NET web service) takes too long to complete, the overall operation could suffer, especially if it fails to ever complete. Per questo motivo, è importante poter introdurre un timeout quando si è in attesa di un'operazione asincrona.For this reason, it’s important to be able to time out when waiting on an asynchronous operation. I metodi sincroni Task.Wait, Task.WaitAll e Task.WaitAny accettano i valori di timeout, ma i corrispondenti metodi TaskFactory.ContinueWhenAll/Task.WhenAny e Task.WhenAll/Task.WhenAny citati in precedenza non li accettano.The synchronous Task.Wait, Task.WaitAll, and Task.WaitAny methods accept time-out values, but the corresponding TaskFactory.ContinueWhenAll/Task.WhenAny and the previously mentioned Task.WhenAll/Task.WhenAny methods do not. È invece possibile usare Task.Delay e Task.WhenAny in combinazione per implementare un timeout.Instead, you can use Task.Delay and Task.WhenAny in combination to implement a time-out.

In un'applicazione con interfaccia utente si desidera, ad esempio, scaricare un'immagine e disabilitare l'interfaccia utente durante il download dell'immagine.For example, in your UI application, let's say that you want to download an image and disable the UI while the image is downloading. Se però il download è troppo lungo, si desidera abilitare nuovamente l'interfaccia utente e rimuovere il download:However, if the download takes too long, you want to re-enable the UI and discard the download:

public async void btnDownload_Click(object sender, EventArgs e)  
{  
    btnDownload.Enabled = false;  
    try  
    {  
        Task<Bitmap> download = GetBitmapAsync(url);  
        if (download == await Task.WhenAny(download, Task.Delay(3000)))  
        {  
            Bitmap bmp = await download;  
            pictureBox.Image = bmp;  
            status.Text = "Downloaded";  
        }  
        else  
        {  
            pictureBox.Image = null;  
            status.Text = "Timed out";  
            var ignored = download.ContinueWith(  
                t => Trace("Task finally completed"));  
        }  
    }  
    finally { btnDownload.Enabled = true; }  
}  

Lo stesso accade con più download, perché WhenAll restituisce un'attività:The same applies to multiple downloads, because WhenAll returns a task:

public async void btnDownload_Click(object sender, RoutedEventArgs e)  
{  
    btnDownload.Enabled = false;  
    try  
    {  
        Task<Bitmap[]> downloads =   
            Task.WhenAll(from url in urls select GetBitmapAsync(url));  
        if (downloads == await Task.WhenAny(downloads, Task.Delay(3000)))  
        {  
            foreach(var bmp in downloads) panel.AddImage(bmp);  
            status.Text = "Downloaded";  
        }  
        else  
        {  
            status.Text = "Timed out";  
            downloads.ContinueWith(t => Log(t));  
        }  
    }  
    finally { btnDownload.Enabled = true; }  
}  

Sviluppo di combinatori basati su attivitàBuilding Task-based Combinators

Poiché un'attività può rappresentare completamente un'operazione asincrona e fornire funzionalità sincrone e asincrone per effettuare join con l'operazione, recuperare i risultati e così via, è possibile compilare utili librerie dei combinatori che costituiscono le attività per costruire modelli più grandi.Because a task is able to completely represent an asynchronous operation and provide synchronous and asynchronous capabilities for joining with the operation, retrieving its results, and so on, you can build useful libraries of combinators that compose tasks to build larger patterns. Come illustrato nella sezione precedente, .NET Framework include diversi combinatori incorporati, ma è possibile costruirne di personalizzati.As discussed in the previous section, the .NET Framework includes several built-in combinators, but you can also build your own. Le sezioni seguenti forniscono alcuni esempi di potenziali metodi e tipi di combinatori.The following sections provide several examples of potential combinator methods and types.

RetryOnFaultRetryOnFault

In molte situazioni è possibile ritentare un'operazione se un tentativo precedente ha dato esito negativo.In many situations, you may want to retry an operation if a previous attempt fails. Per il codice sincrono, è possibile creare un metodo helper, ad esempio RetryOnFault come mostrato nell'esempio seguente, per eseguire questa operazione:For synchronous code, you might build a helper method such as RetryOnFault in the following example to accomplish this:

public static T RetryOnFault<T>(  
    Func<T> function, int maxTries)  
{  
    for(int i=0; i<maxTries; i++)  
    {  
        try { return function(); }  
        catch { if (i == maxTries-1) throw; }  
    }  
    return default(T);  
}  

È possibile compilare un metodo helper quasi identico per le operazioni asincrone implementate con TAP e restituire in questo modo delle attività:You can build an almost identical helper method for asynchronous operations that are implemented with TAP and thus return tasks:

public static async Task<T> RetryOnFault<T>(  
    Func<Task<T>> function, int maxTries)  
{  
    for(int i=0; i<maxTries; i++)  
    {  
        try { return await function().ConfigureAwait(false); }  
        catch { if (i == maxTries-1) throw; }  
    }  
    return default(T);  
}  

È successivamente possibile usare questo combinatore per codificare i tentativi nella logica dell'applicazione. Ad esempio:You can then use this combinator to encode retries into the application’s logic; for example:

// Download the URL, trying up to three times in case of failure  
string pageContents = await RetryOnFault(  
    () => DownloadStringAsync(url), 3);  

È possibile estendere ulteriormente la funzione RetryOnFault.You could extend the RetryOnFault function further. La funzione potrebbe, ad esempio, accettare un'altra Func<Task> che verrà richiamata tra i nuovi tentativi per determinare quando eseguire nuovamente l'operazione. Ad esempio:For example, the function could accept another Func<Task> that will be invoked between retries to determine when to try the operation again; for example:

public static async Task<T> RetryOnFault<T>(  
    Func<Task<T>> function, int maxTries, Func<Task> retryWhen)  
{  
    for(int i=0; i<maxTries; i++)  
    {  
        try { return await function().ConfigureAwait(false); }  
        catch { if (i == maxTries-1) throw; }  
        await retryWhen().ConfigureAwait(false);  
    }  
    return default(T);  
}  

È quindi possibile usare la funzione come descritto di seguito per attendere un secondo prima di ritentare l'operazione:You could then use the function as follows to wait for a second before retrying the operation:

// Download the URL, trying up to three times in case of failure,  
// and delaying for a second between retries  
string pageContents = await RetryOnFault(  
    () => DownloadStringAsync(url), 3, () => Task.Delay(1000));  

NeedOnlyOneNeedOnlyOne

È talvolta possibile usare la ridondanza per migliorare la latenza e le probabilità di successo di un'operazione.Sometimes, you can take advantage of redundancy to improve an operation’s latency and chances for success. Si considerino vari servizi Web che forniscono quotazioni azionarie. In diversi momenti del giorno ogni servizio può fornire diversi livelli di qualità e tempi di risposta.Consider multiple web services that provide stock quotes, but at various times of the day, each service may provide different levels of quality and response times. Per la gestione di tali fluttuazioni, è possibile inviare richieste a tutti i servizi Web e non appena si riceve una risposta da una, annullare le richieste rimanenti.To deal with these fluctuations, you may issue requests to all the web services, and as soon as you get a response from one, cancel the remaining requests. È possibile implementare una funzione helper per semplificare l'implementazione di questo modello comune basato sull'avvio di più operazioni, l'attesa della prima risposta e l'annullamento delle operazioni che non hanno fornito la risposta per prime.You can implement a helper function to make it easier to implement this common pattern of launching multiple operations, waiting for any, and then canceling the rest. La funzione NeedOnlyOne dell'esempio seguente illustra questo scenario:The NeedOnlyOne function in the following example illustrates this scenario:

public static async Task<T> NeedOnlyOne(  
    params Func<CancellationToken,Task<T>> [] functions)  
{  
    var cts = new CancellationTokenSource();  
    var tasks = (from function in functions  
                 select function(cts.Token)).ToArray();  
    var completed = await Task.WhenAny(tasks).ConfigureAwait(false);  
    cts.Cancel();  
    foreach(var task in tasks)   
    {  
        var ignored = task.ContinueWith(  
            t => Log(t), TaskContinuationOptions.OnlyOnFaulted);  
    }  
    return completed;  
}  

È possibile quindi usare questa funzione come segue:You can then use this function as follows:

double currentPrice = await NeedOnlyOne(  
    ct => GetCurrentPriceFromServer1Async("msft", ct),  
    ct => GetCurrentPriceFromServer2Async("msft", ct),  
    ct => GetCurrentPriceFromServer3Async("msft", ct));  

Operazioni con interfoliazioneInterleaved Operations

Esiste un potenziale problema di prestazioni quando si usa il metodo WhenAny per supportare uno scenario di interfoliazione nel caso in cui si usino set di attività molto grandi.There is a potential performance problem with using the WhenAny method to support an interleaving scenario when you're working with very large sets of tasks. Ogni chiamata a WhenAny comporta la registrazione di una continuazione con ogni attività.Every call to WhenAny results in a continuation being registered with each task. Per un numero N di attività, questo comporta O(N2) continuazioni create durante l'operazione di interfoliazione.For N number of tasks, this results in O(N2) continuations created over the lifetime of the interleaving operation. Se si utilizza un ampio set di attività, è possibile utilizzare un operatore combinatorio (nell'esempio seguente Interleaved) per risolvere il problema di prestazioni:If you're working with a large set of tasks, you can use a combinator (Interleaved in the following example) to address the performance issue:

static IEnumerable<Task<T>> Interleaved<T>(IEnumerable<Task<T>> tasks)  
{  
    var inputTasks = tasks.ToList();  
    var sources = (from _ in Enumerable.Range(0, inputTasks.Count)   
                   select new TaskCompletionSource<T>()).ToList();  
    int nextTaskIndex = -1;  
    foreach (var inputTask in inputTasks)  
    {  
        inputTask.ContinueWith(completed =>  
        {  
            var source = sources[Interlocked.Increment(ref nextTaskIndex)];  
            if (completed.IsFaulted)   
                source.TrySetException(completed.Exception.InnerExceptions);  
            else if (completed.IsCanceled)   
                source.TrySetCanceled();  
            else   
                source.TrySetResult(completed.Result);  
        }, CancellationToken.None,   
           TaskContinuationOptions.ExecuteSynchronously,   
           TaskScheduler.Default);  
    }  
    return from source in sources   
           select source.Task;  
}  

È quindi possibile usare il combinatore per elaborare i risultati delle attività man mano che vengono completate. Ad esempio:You can then use the combinator to process the results of tasks as they complete; for example:

IEnumerable<Task<int>> tasks = ...;  
foreach(var task in Interleaved(tasks))  
{  
    int result = await task;  
    …  
}  

WhenAllOrFirstExceptionWhenAllOrFirstException

In alcuni scenari di dispersione/raccolta di dati, potrebbe essere necessario attendere il completamento di tutti i processi di un set, a meno che uno di questi non fallisca, nel qual caso si può arrestare l'attesa non appena si verifica l'eccezione.In certain scatter/gather scenarios, you might want to wait for all tasks in a set, unless one of them faults, in which case you want to stop waiting as soon as the exception occurs. È possibile eseguire questa operazione con un metodo combinatore, ad esempio WhenAllOrFirstException, come illustrato nell'esempio seguente:You can accomplish that with a combinator method such as WhenAllOrFirstException in the following example:

public static Task<T[]> WhenAllOrFirstException<T>(IEnumerable<Task<T>> tasks)  
{  
    var inputs = tasks.ToList();  
    var ce = new CountdownEvent(inputs.Count);  
    var tcs = new TaskCompletionSource<T[]>();  

    Action<Task> onCompleted = (Task completed) =>  
    {  
        if (completed.IsFaulted)   
            tcs.TrySetException(completed.Exception.InnerExceptions);  
        if (ce.Signal() && !tcs.Task.IsCompleted)  
            tcs.TrySetResult(inputs.Select(t => t.Result).ToArray());  
    };  

    foreach (var t in inputs) t.ContinueWith(onCompleted);  
    return tcs.Task;  
}  

Creazione di strutture dei dati basate su attivitàBuilding Task-based Data Structures

Oltre alla possibilità di costruire combinatori personalizzati basati su attività, avere una struttura dei dati in Task e Task<TResult> che rappresenta sia i risultati di un'operazione asincrona che la sincronizzazione necessaria con cui creare un join è una soluzione molto potente su cui basare la creazione di strutture dei dati personalizzate da usare in scenari asincroni.In addition to the ability to build custom task-based combinators, having a data structure in Task and Task<TResult> that represents both the results of an asynchronous operation and the necessary synchronization to join with it makes it a very powerful type on which to build custom data structures to be used in asynchronous scenarios.

AsyncCacheAsyncCache

Un aspetto importante di un'attività è che può essere distribuita a diversi clienti, tutti la possono attendere, registrarvi continuazioni, ottenerne il risultato o le eccezioni (nel caso di Task<TResult>) e così via.One important aspect of a task is that it may be handed out to multiple consumers, all of whom may await it, register continuations with it, get its result or exceptions (in the case of Task<TResult>), and so on. Ciò rende Task e Task<TResult> ideali per essere usati in un'infrastruttura asincrona di memorizzazione nella cache.This makes Task and Task<TResult> perfectly suited to be used in an asynchronous caching infrastructure. Di seguito è riportato un esempio di una piccola ma potente cache asincrona costruita basandosi su Task<TResult>:Here’s an example of a small but powerful asynchronous cache built on top of Task<TResult>:

public class AsyncCache<TKey, TValue>  
{  
    private readonly Func<TKey, Task<TValue>> _valueFactory;  
    private readonly ConcurrentDictionary<TKey, Lazy<Task<TValue>>> _map;  

    public AsyncCache(Func<TKey, Task<TValue>> valueFactory)  
    {  
        if (valueFactory == null) throw new ArgumentNullException("loader");  
        _valueFactory = valueFactory;  
        _map = new ConcurrentDictionary<TKey, Lazy<Task<TValue>>>();  
    }  

    public Task<TValue> this[TKey key]  
    {  
        get  
        {  
            if (key == null) throw new ArgumentNullException("key");  
            return _map.GetOrAdd(key, toAdd =>   
                new Lazy<Task<TValue>>(() => _valueFactory(toAdd))).Value;  
        }  
    }  
}  

La classe AsyncCache<TKey, TValue> accetta come delegato al costruttore una funzione che accetta TKey e restituisce Task<TResult>.The AsyncCache<TKey,TValue> class accepts as a delegate to its constructor a function that takes a TKey and returns a Task<TResult>. Tutti i valori della cache a cui si è acceduto precedentemente vengono archiviati nel dizionario interno e AsyncCache fa in modo che venga generata solo un'attività per chiave, anche si accede alla cache contemporaneamente.Any previously accessed values from the cache are stored in the internal dictionary, and the AsyncCache ensures that only one task is generated per key, even if the cache is accessed concurrently.

È possibile, ad esempio, creare una cache per le pagine Web scaricate:For example, you can build a cache for downloaded web pages:

private AsyncCache<string,string> m_webPages =   
    new AsyncCache<string,string>(DownloadStringAsync);  

È quindi possibile usare tale cache nei metodi asincroni ogni volta che è necessario accedere al contenuto di una pagina Web.You can then use this cache in asynchronous methods whenever you need the contents of a web page. La classe AsyncCache assicura che si scarichi il minor numero di pagine possibile e memorizza i risultati nella cache.The AsyncCache class ensures that you’re downloading as few pages as possible, and caches the results.

private async void btnDownload_Click(object sender, RoutedEventArgs e)   
{  
    btnDownload.IsEnabled = false;  
    try  
    {  
        txtContents.Text = await m_webPages["http://www.microsoft.com"];  
    }  
    finally { btnDownload.IsEnabled = true; }  
}  

AsyncProducerConsumerCollectionAsyncProducerConsumerCollection

È inoltre possibile usare attività per creare strutture dei dati per coordinare le attività asincrone.You can also use tasks to build data structures for coordinating asynchronous activities. Si consideri uno dei classici modelli di progettazione paralleli: produttore/consumatore.Consider one of the classic parallel design patterns: producer/consumer. In questo modello, i produttori generano dati che sono consumati dai consumatori e i produttori e i consumatori possono operare in parallelo.In this pattern, producers generate data that is consumed by consumers, and the producers and consumers may run in parallel. Il consumatore elabora, ad esempio, l'elemento 1, che è stato precedentemente generato da un produttore che sta scrivendo l'elemento 2.For example, the consumer processes item 1, which was previously generated by a producer who is now producing item 2. Il modello produttore/consumatore prevede invariabilmente la presenza di una struttura dei dati in cui memorizzare il lavoro creato dai produttori in modo che i consumatori possano essere informati di nuovi dati e possano gestirli una volta disponibili.For the producer/consumer pattern, you invariably need some data structure to store the work created by producers so that the consumers may be notified of new data and find it when available.

Questa è una semplice struttura dei dati costruita basandosi su attività che consente di usare i metodi asincroni come produttori e consumatori:Here’s a simple data structure built on top of tasks that enables asynchronous methods to be used as producers and consumers:

public class AsyncProducerConsumerCollection<T>  
{  
    private readonly Queue<T> m_collection = new Queue<T>();  
    private readonly Queue<TaskCompletionSource<T>> m_waiting =   
        new Queue<TaskCompletionSource<T>>();  

    public void Add(T item)  
    {  
        TaskCompletionSource<T> tcs = null;  
        lock (m_collection)  
        {  
            if (m_waiting.Count > 0) tcs = m_waiting.Dequeue();  
            else m_collection.Enqueue(item);  
        }  
        if (tcs != null) tcs.TrySetResult(item);  
    }  

    public Task<T> Take()  
    {  
        lock (m_collection)  
        {  
            if (m_collection.Count > 0)   
            {  
                return Task.FromResult(m_collection.Dequeue());   
            }  
            else   
            {  
                var tcs = new TaskCompletionSource<T>();  
                m_waiting.Enqueue(tcs);  
                return tcs.Task;  
            }  
        }  
    }  
}  

Con tale struttura dei dati è possibile scrivere codice come il seguente:With that data structure in place, you can write code such as the following:

private static AsyncProducerConsumerCollection<int> m_data = …;  
…  
private static async Task ConsumerAsync()  
{  
    while(true)  
    {  
        int nextItem = await m_data.Take();  
        ProcessNextItem(nextItem);  
    }  
}  
…  
private static void Produce(int data)  
{  
    m_data.Add(data);  
}  

Lo spazio dei nomi System.Threading.Tasks.Dataflow include il tipo BufferBlock<T>, che è possibile usare in modo simile, ma senza dover compilare un tipo di raccolta personalizzato:The System.Threading.Tasks.Dataflow namespace includes the BufferBlock<T> type, which you can use in a similar manner, but without having to build a custom collection type:

private static BufferBlock<int> m_data = …;  
…  
private static async Task ConsumerAsync()  
{  
    while(true)  
    {  
        int nextItem = await m_data.ReceiveAsync();  
        ProcessNextItem(nextItem);  
    }  
}  
…  
private static void Produce(int data)  
{  
    m_data.Post(data);  
}  

Nota

Lo spazio dei nomi System.Threading.Tasks.Dataflow è disponibile in .NET Framework 4.5.NET Framework 4.5 tramite NuGet.The System.Threading.Tasks.Dataflow namespace is available in the .NET Framework 4.5.NET Framework 4.5 through NuGet. Per installare l'assembly contenente lo spazio dei nomi System.Threading.Tasks.Dataflow, aprire il progetto in Visual Studio 2012Visual Studio 2012, scegliere Gestisci pacchetti NuGet dal menu Progetto e cercare online il pacchetto Microsoft.Tpl.Dataflow.To install the assembly that contains the System.Threading.Tasks.Dataflow namespace, open your project in Visual Studio 2012Visual Studio 2012, choose Manage NuGet Packages from the Project menu, and search online for the Microsoft.Tpl.Dataflow package.

Vedere ancheSee Also

Modello asincrono basato su attività (TAP)Task-based Asynchronous Pattern (TAP)
Implementazione del modello asincrono basato su attivitàImplementing the Task-based Asynchronous Pattern
Interoperabilità con altri tipi e modelli asincroniInterop with Other Asynchronous Patterns and Types