Consumindo o padrão assíncrono baseado em tarefaConsuming the Task-based Asynchronous Pattern

Quando você usa o TAP (padrão assíncrono baseado em tarefa) para trabalhar com operações assíncronas, você pode usar retornos de chamada para obter uma espera sem bloqueio.When you use the Task-based Asynchronous Pattern (TAP) to work with asynchronous operations, you can use callbacks to achieve waiting without blocking. Para tarefas, isso é conseguido por meio de métodos como Task.ContinueWith.For tasks, this is achieved through methods such as Task.ContinueWith. O suporte assíncrono baseado em linguagem oculta retornos de chamada ao permitir que operações assíncronas sejam colocadas em espera no fluxo de controle normal, sendo que o código gerado pelo compilador fornece esse mesmo suporte de nível de 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.

Suspendendo a execução com AwaitSuspending Execution with Await

Começando com o .NET Framework 4.5, você pode usar a palavra-chave await em C# e o Operador Await no Visual Basic para aguardar os objetos Task e Task<TResult> de modo assíncrono.Starting with the .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 você está aguardando um Task, a expressão await é do tipo void.When you're awaiting a Task, the await expression is of type void. Quando você está aguardando um Task<TResult>, a expressão await é do tipo TResult.When you're awaiting a Task<TResult>, the await expression is of type TResult. Uma expressão await deve ocorrer dentro do corpo de um método assíncrono.An await expression must occur inside the body of an asynchronous method. Para obter mais informações sobre suporte às linguagens C# e Visual Basic no .NET Framework 4.5, consulte as especificações das linguagens C# e Visual Basic.For more information about C# and Visual Basic language support in the .NET Framework 4.5, see the C# and Visual Basic language specifications.

Nos bastidores, a funcionalidade await instala um retorno de chamada na tarefa pelo uso de uma continuação.Under the covers, the await functionality installs a callback on the task by using a continuation. Esse retorno de chamada retoma o método assíncrono no ponto de suspensão.This callback resumes the asynchronous method at the point of suspension. Quando o método assíncrono é retomado, se a operação de espera foi concluída com êxito e era uma Task<TResult>, o respectivo TResult é retornado.When the asynchronous method is resumed, if the awaited operation completed successfully and was a Task<TResult>, its TResult is returned. Se o Task ou Task<TResult> que foi aguardado terminou no estado Canceled, uma exceção OperationCanceledException será lançada.If the Task or Task<TResult> that was awaited ended in the Canceled state, an OperationCanceledException exception is thrown. Se o Task ou Task<TResult> que foi aguardado terminou no estado Faulted, uma exceção que causou a falha será lançada.If the Task or Task<TResult> that was awaited ended in the Faulted state, the exception that caused it to fault is thrown. Uma Task pode falhar como resultado de várias exceções, mas apenas uma dessas exceções é propagada.A Task can fault as a result of multiple exceptions, but only one of these exceptions is propagated. No entanto, a propriedade Task.Exception retorna uma exceção AggregateException que contém todos os erros.However, the Task.Exception property returns an AggregateException exception that contains all the errors.

Se um contexto de sincronização (objeto SynchronizationContext) é associado ao thread que estava executando o método assíncrono no momento da suspensão (por exemplo, se a propriedade SynchronizationContext.Current não é null), o método assíncrono é retomado no mesmo contexto de sincronização usando o método Post do contexto.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. Caso contrário, ele utiliza o Agendador de Tarefas (objeto TaskScheduler) que era atual no momento da suspensão.Otherwise, it relies on the task scheduler (TaskScheduler object) that was current at the time of suspension. Normalmente, esse é que o agendador de tarefas padrão (TaskScheduler.Default), que tem como alvo o pool de threads.Typically, this is the default task scheduler (TaskScheduler.Default), which targets the thread pool. O Agendador de Tarefas determina se a operação assíncrona aguardada deve continuar do ponto em que foi concluída ou se a retomada deve ser agendada.This task scheduler determines whether the awaited asynchronous operation should resume where it completed or whether the resumption should be scheduled. O agendador padrão geralmente permite que a continuação seja executada no thread em que a operação aguardada foi concluída.The default scheduler typically allows the continuation to run on the thread that the awaited operation completed.

Quando um método assíncrono é chamado, ele executa o corpo da função de modo síncrono até a primeira expressão await em uma instância aguardável que ainda não tenha foi concluída, no ponto em que a invocação retorna ao chamador.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 o método assíncrono não retornar void, um objeto Task ou Task<TResult> será retornado para representar o cálculo em andamento.If the asynchronous method does not return void, a Task or Task<TResult> object is returned to represent the ongoing computation. Em um método assíncrono não nulo, se uma instrução return for encontrada ou o final do corpo do método for atingido, a tarefa será concluída no estado final 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 uma exceção sem tratamento fizer com que o controle deixe o corpo do método assíncrono, a tarefa terminará no estado Faulted.If an unhandled exception causes control to leave the body of the asynchronous method, the task ends in the Faulted state. Se essa exceção for um OperationCanceledException, a tarefa terminará no estado Canceled.If that exception is an OperationCanceledException, the task instead ends in the Canceled state. Dessa maneira, a exceção ou o resultado é eventualmente publicado.In this manner, the result or exception is eventually published.

Há diversas variações importantes desse comportamento.There are several important variations of this behavior. Por motivos de desempenho, se uma tarefa já foi concluída até o momento em que a tarefa é aguardada, o controle não é suspenso e a função continua a executar.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. Além disso, retornar para o contexto original nem sempre é o comportamento desejado e pode ser alterado; isso é descrito mais detalhadamente na próxima seção.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.

Configurando a suspensão e retomada com Yield e ConfigureAwaitConfiguring Suspension and Resumption with Yield and ConfigureAwait

Vários métodos fornecem mais controle sobre a execução de um método assíncrono.Several methods provide more control over an asynchronous method’s execution. Por exemplo, você pode usar o método Task.Yield para introduzir um ponto de rendimento para o método assíncrono: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();
    …
}

Isso é equivalente a postar assincronamente ou agendar de volta para o contexto atual.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
        ...
    }
});

Você também pode usar o método Task.ConfigureAwait para controlar melhor a suspensão e a retomada de um método assíncrono.You can also use the Task.ConfigureAwait method for better control over suspension and resumption in an asynchronous method. Conforme mencionado anteriormente, por padrão, o contexto atual é capturado no momento em que um método assíncrono é suspenso e esse contexto capturado é usado para invocar a continuação do método assíncrono após a retomada.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. Em muitos casos, esse é o comportamento exato desejado.In many cases, this is the exact behavior you want. Em outros casos, você pode não se importar com o contexto de continuação e pode obter o melhor desempenho ao evitar essas postagens de volta para o contexto original.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. Para habilitar isso, use o método Task.ConfigureAwait para informar à operação de espera que não capture e retome no contexto, mas sim continue a execução no ponto em que a operação assíncrona que estava sendo aguardada foi concluída, seja qual for esse ponto: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);

Cancelando uma operação assíncronaCanceling an Asynchronous Operation

A partir do NET Framework 4, os métodos TAP que dão suporte ao cancelamento fornecem pelo menos uma sobrecarga que aceita um token de cancelamento (objeto CancellationToken).Starting with the .NET Framework 4, TAP methods that support cancellation provide at least one overload that accepts a cancellation token (CancellationToken object).

Um token de cancelamento é criado por meio de uma origem de token de cancelamento (objeto CancellationTokenSource).A cancellation token is created through a cancellation token source (CancellationTokenSource object). A propriedade Token da fonte retorna o token de cancelamento que sinalizará quando o método Cancel da fonte será chamado.The source’s Token property returns the cancellation token that will be signaled when the source’s Cancel method is called. Por exemplo, se você desejar baixar uma única página da Web e você deseja ser capaz de cancelar a operação, crie um objeto CancellationTokenSource, passe o token desse objeto para o método TAP e, em seguida, chame o método Cancel da origem quando estiver pronto para cancelar a operação: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();

Para cancelar várias invocações assíncronas, você pode passar o mesmo token para todas as invocações: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();

Ou então, você pode passar o mesmo token para um subconjunto seletivo de operações: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();

Solicitações de cancelamento podem ser iniciadas de qualquer thread.Cancellation requests may be initiated from any thread.

Você pode passar o valor CancellationToken.None a qualquer método que aceite um token de cancelamento para indicar que o cancelamento nunca será solicitado.You can pass the CancellationToken.None value to any method that accepts a cancellation token to indicate that cancellation will never be requested. Isso faz a propriedade CancellationToken.CanBeCanceled retornar false, e o método chamado pode ser otimizado adequadamente.This causes the CancellationToken.CanBeCanceled property to return false, and the called method can optimize accordingly. Para fins de teste, você também pode passar um token de cancelamento previamente cancelado que é instanciado pelo uso do construtor que aceita um valor booliano para indicar se o token deve iniciar em um estado não cancelável ou já cancelado.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.

Essa abordagem para o cancelamento tem várias vantagens:This approach to cancellation has several advantages:

  • Você pode passar o mesmo token de cancelamento para qualquer número de operações síncronas e assíncronas.You can pass the same cancellation token to any number of asynchronous and synchronous operations.

  • A mesma solicitação de cancelamento pode ser proliferada para qualquer número de ouvintes.The same cancellation request may be proliferated to any number of listeners.

  • O desenvolvedor da API assíncrona tem total controle com relação ao cancelamento poder ou não ser solicitado e a quando isso pode entrar em vigor.The developer of the asynchronous API is in complete control of whether cancellation may be requested and when it may take effect.

  • O código que consome a API pode, seletivamente, determinar as invocações assíncronas para as quais as solicitações de cancelamento serão propagadas.The code that consumes the API may selectively determine the asynchronous invocations that cancellation requests will be propagated to.

Monitorando o progressoMonitoring Progress

Alguns métodos assíncronos expõem progresso através de uma interface de progresso passada para o método assíncrono.Some asynchronous methods expose progress through a progress interface passed into the asynchronous method. Por exemplo, considere uma função que baixa de forma assíncrona uma cadeia de caracteres de texto e, durante esse processo, emite atualizações de progresso que incluem o percentual de download concluído até o 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. Tal método poderia ser consumido em um aplicativo do WPF (Windows Presentation Foundation) da seguinte maneira: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; }
}

Usando os combinadores baseados em tarefas internosUsing the Built-in Task-based Combinators

O namespace System.Threading.Tasks inclui vários métodos para compor tarefas e trabalhar com elas.The System.Threading.Tasks namespace includes several methods for composing and working with tasks.

Task.RunTask.Run

A classe Task inclui vários métodos Run que permitem descarregar com facilidade o trabalho como um Task ou Task<TResult> para o pool de threads, por exemplo: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;
    });
}

Alguns desses métodos Run, como a sobrecarga de Task.Run(Func<Task>), existe como um atalho para o método TaskFactory.StartNew.Some of these Run methods, such as the Task.Run(Func<Task>) overload, exist as shorthand for the TaskFactory.StartNew method. Outras sobrecargas, como Task.Run(Func<Task>), permitem que você use await dentro do trabalho descarregado, por exemplo: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);
    });
}

Essas sobrecargas são logicamente equivalentes a usar o método TaskFactory.StartNew junto com o método de extensão Unwrap na biblioteca de paralelismo de tarefas.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

Use o método FromResult em cenários nos quais os dados talvez já estejam disponíveis e apenas precisem ser retornados de um método de retorno de tarefas elevado para uma 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

Use o método WhenAll para aguardar de forma assíncrona várias operações assíncronas que são representadas como tarefas.Use the WhenAll method to asynchronously wait on multiple asynchronous operations that are represented as tasks. O método tem várias sobrecargas que dão suporte a um conjunto de tarefas não genéricas ou um conjunto não uniforme de tarefas genéricas (por exemplo, aguardar de forma assíncrona várias operações que retornam nulo ou aguardar vários métodos que retornam um valor em que cada valor pode ter um tipo diferente) e dão suporte a um conjunto uniforme de tarefas genéricas (como aguardar de forma assíncrona vários métodos que retornam 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).

Digamos que você deseja enviar mensagens de email para vários clientes.Let's say you want to send email messages to several customers. Você pode sobrepor o envio de mensagens de modo que você não aguarde a conclusão de uma mensagem antes de enviar a próxima.You can overlap sending the messages so you're not waiting for one message to complete before sending the next. Você também pode descobrir quando as operações de envio foram concluídas e se ocorreu algum erro: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);

Esse código não tratará explicitamente exceções que possam ocorrer, mas permitirá que as exceções sejam propagadas para fora do await na tarefa resultante de 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. Para tratar as exceções, você pode usar código como o seguinte: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)
{
    ...
}

Nesse caso, se alguma operação assíncrona falhar, todas as exceções serão consolidadas em uma exceção AggregateException, que é armazenada no Task que é retornado do método 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. No entanto, apenas uma dessas exceções é propagada pela palavra-chave await.However, only one of those exceptions is propagated by the await keyword. Se você quiser examinar todas as exceções, poderá reescrever o código anterior da seguinte maneira: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
    }
}

Vamos considerar um exemplo em que baixamos vários arquivos da Web de forma assíncrona.Let's consider an example of downloading multiple files from the web asynchronously. Nesse caso, todas as operações assíncronas têm tipos de resultado homogêneos e é fácil acessar os resultados: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));

Você pode usar as mesmas técnicas de tratamento de exceções discutidas no cenário anterior, em que nulo é retornado: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

Você pode usar o método WhenAny para aguardar de forma assíncrona apenas uma de várias operações assíncronas que são representadas como tarefas.You can use the WhenAny method to asynchronously wait for just one of multiple asynchronous operations represented as tasks to complete. Esse método tem quatro casos de uso principais:This method serves four primary use cases:

  • Redundância: executar uma operação várias vezes e selecionar aquela que é concluída primeiro (por exemplo, entrando em contato com vários serviços Web de cotação de ações que produzirão um único resultado e selecionar aquele que termina mais rápido).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).

  • Intercalação: iniciar várias operações e aguardar que todas eles sejam concluídas, mas processando-as conforme são concluídas.Interleaving: Launching multiple operations and waiting for all of them to complete, but processing them as they complete.

  • Limitação: permitir que operações adicionais comecem conforme outras forem concluídas.Throttling: Allowing additional operations to begin as others complete. Essa é uma extensão do cenário de intercalação.This is an extension of the interleaving scenario.

  • Saída precoce: por exemplo, uma operação representada pela tarefa t1 pode ser agrupada em uma tarefa WhenAny com outra tarefa t2 e você pode aguardar na tarefa 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. A tarefa t2 pode representar um tempo limite, um cancelamento ou algum outro sinal que faça com que a tarefa WhenAny seja concluída antes de t1.Task t2 could represent a time-out, or cancellation, or some other signal that causes the WhenAny task to complete before t1 completes.

RedundânciaRedundancy

Considere um caso em que você deseja tomar uma decisão sobre a possibilidade de comprar uma ação.Consider a case where you want to make a decision about whether to buy a stock. Há diversos serviços Web de recomendação de compra de ações nos quais você confia, mas dependendo da carga diária, cada serviço pode acabar sendo lento em horários diferentes.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. Você pode usar o método WhenAny para receber uma notificação quando qualquer operação for concluída: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);

Ao contrário de WhenAll, que retorna os resultados não encapsulados de todas as tarefas concluídas com êxito, o WhenAny retorna a tarefa que foi concluída.Unlike WhenAll, which returns the unwrapped results of all tasks that completed successfully, WhenAny returns the task that completed. Se uma tarefa falhar, será importante saber que ela falhou e, se uma tarefa for bem-sucedida, será importante saber a qual tarefa o valor retornado é associado.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. Portanto, você precisa acessar o resultado da tarefa retornada ou aguardá-la ainda mais, conforme mostra este exemplo.Therefore, you need to access the result of the returned task, or further await it, as this example shows.

Assim como acontece com o WhenAll, você precisa ser capaz de acomodar as exceções.As with WhenAll, you have to be able to accommodate exceptions. Devido a você receber novamente a tarefa concluída, você poderá esperar a tarefa retornada para então propagar os erros e aplicar try/catch a eles adequadamente; por exemplo: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);
    }
}

Além disso, mesmo que uma primeira tarefa seja concluída com êxito, as tarefas subsequentes poderão falhar.Additionally, even if a first task completes successfully, subsequent tasks may fail. Neste ponto, você tem várias opções para lidar com exceções: você pode esperar até que todas as tarefas iniciadas tenham sido concluídas, caso em que você pode usar o método WhenAll, ou você pode decidir que todas as exceções são importantes e devem estar conectadas.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. Para isso, você pode usar as continuações para receber uma notificação quando tarefas foram concluídas de forma assíncrona: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); });
}

ou:or:

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

ou até mesmo: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);

Finalmente, você talvez queira cancelar todas as operações restantes: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);

IntercalaçãoInterleaving

Considere um caso em que você está baixando imagens da Web e processando cada imagem (por exemplo, adicionando a imagem a um controle de interface do usuário).Consider a case where you're downloading images from the web and processing each image (for example, adding the image to a UI control). Você precisa fazer o processamento sequencialmente no thread da interface do usuário, mas você quer baixar as imagens tão simultaneamente quanto possível.You have to do the processing sequentially on the UI thread, but you want to download the images as concurrently as possible. Além disso, você não deseja esperar para adicionar as imagens à interface do usuário após elas serem todas baixadas – você deseja adicioná-las conforme o download de cada uma delas é concluído: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{}
}

Você também pode aplicar interpolação para um cenário que envolve o processamento de recursos computacionais no ThreadPool das imagens baixadas; por exemplo: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{}
}

LimitaçãoThrottling

Considere o exemplo de intercalação, exceto que o usuário está baixando um número tão grande de imagens que os downloads precisam ser limitados; por exemplo, você deseja que apenas um número específico de downloads ocorram simultaneamente.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. Para conseguir isso, você pode iniciar um subconjunto das operações assíncronas.To achieve this, you can start a subset of the asynchronous operations. Conforme as operações forem concluídas, você pode iniciar operações adicionais para assumir o lugar delas: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++;
    }
}

Saída precoceEarly Bailout

Considere a possibilidade de que você está aguardando de forma assíncrona que uma operação seja concluída e, simultaneamente, respondendo a uma solicitação de cancelamento do usuário (por exemplo, o usuário clicou em um botão para cancelar).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). O código a seguir ilustra esse cenário: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;
}

Essa implementação reabilita a interface do usuário assim que você decide sair, mas não as cancela as operações assíncronas subjacentes.This implementation re-enables the user interface as soon as you decide to bail out, but doesn't cancel the underlying asynchronous operations. Outra alternativa seria cancelar as operações pendentes quando você decide sair, mas não restabelecer a interface do usuário até que as operações sejam realmente concluídas, possivelmente devido a um encerramento precoce devido à solicitação de cancelamento: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; }
}

Outro exemplo de saída precoce envolve o uso do método WhenAny junto com o método Delay, conforme discutido na próxima seção.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

Você pode usar o método Task.Delay para apresentar pausas em uma execução de um método assíncrono.You can use the Task.Delay method to introduce pauses into an asynchronous method’s execution. Isso é útil para muitos tipos de funcionalidades, incluindo o build de loops de sondagem e o atraso da manipulação da entrada do usuário por um período de tempo predeterminado.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. O método Task.Delay também pode ser útil em combinação com o Task.WhenAny para implementação de tempos limite em esperas.The Task.Delay method can also be useful in combination with Task.WhenAny for implementing time-outs on awaits.

Se uma tarefa que for parte de uma operação assíncrona maior (por exemplo, um serviço Web ASP.NET) levar muito tempo para concluir, a operação geral poderá ser afetada, especialmente se ela falhar e nunca for concluída.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. Por esse motivo, é importante ser capaz de atingir o tempo limite ao aguardar uma operação assíncrona.For this reason, it’s important to be able to time out when waiting on an asynchronous operation. Os métodos Task.Wait, Task.WaitAll e Task.WaitAny aceitam valores de tempo limite, mas os correspondentes TaskFactory.ContinueWhenAll/Task.WhenAny e os métodos Task.WhenAll/Task.WhenAny mencionados anteriormente não.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. Em vez disso, você pode usar Task.Delay e Task.WhenAny em conjunto para implementar um tempo limite.Instead, you can use Task.Delay and Task.WhenAny in combination to implement a time-out.

Por exemplo, em seu aplicativo de interface do usuário, digamos que você deseja baixar uma imagem e desabilitar a interface do usuário durante esse download.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. No entanto, se o download levar muito tempo, você desejará reabilitar a interface do usuário e descartar o 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; }
}

O mesmo aplica-se a vários downloads, porque WhenAll retorna uma tarefa: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; }
}

Compilando combinadores baseados em tarefaBuilding Task-based Combinators

Já que uma tarefa é capaz de representar totalmente uma operação assíncrona e fornecer recursos síncronos e assíncronos para ingressar na operação, recuperar seus resultados e assim por diante, você pode compilar bibliotecas úteis de combinadores que compõem tarefas para compilar padrões maiores.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. Conforme discutido na seção anterior, o .NET Framework inclui vários combinadores internos, mas você também pode compilar os seus próprios combinadores.As discussed in the previous section, the .NET Framework includes several built-in combinators, but you can also build your own. As seções a seguir fornecem vários exemplos de tipos e métodos combinadores potenciais.The following sections provide several examples of potential combinator methods and types.

RetryOnFaultRetryOnFault

Em muitas situações, talvez você queira repetir uma operação se uma tentativa anterior falhar.In many situations, you may want to retry an operation if a previous attempt fails. Para código síncrono, você pode compilar um método auxiliar como RetryOnFault no exemplo a seguir para fazer isso: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);
}

Você pode compilar um método auxiliar quase idêntico para operações assíncronas implementadas com TAP e, assim, retornar tarefas: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);
}

Você pode usar este combinador para codificar repetições na lógica do aplicativo, por exemplo: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);

Você pode estender ainda mais a função RetryOnFault.You could extend the RetryOnFault function further. Por exemplo, a função poderá aceitar outro Func<Task> que será invocado entre as tentativas para determinar quando tentar novamente a operação, por exemplo: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);
}

Você pode então usar a função da seguinte maneira, para aguardar um segundo antes de repetir a operação: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

Às vezes, você pode tirar proveito da redundância para melhorar a latência e as chances de sucesso de uma operação.Sometimes, you can take advantage of redundancy to improve an operation’s latency and chances for success. Considere vários serviços Web que fornecem cotações de ações, mas em diferentes horas do dia, cada serviço pode fornecer diferentes níveis de qualidade e tempos de resposta.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. Para lidar com essas flutuações, você poderá emitir solicitações para todos os serviços Web e, assim que você obtiver uma resposta de um deles, cancelar as solicitações restantes.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. Você pode implementar uma função auxiliar para facilitar a implementação desse padrão comum de inicialização de várias operações, aguardar uma delas e, em seguida, cancelar o restante.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. A função NeedOnlyOne no exemplo a seguir ilustra esse cenário: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;
}

Você pode então usar essa função conforme demonstrado a seguir:You can then use this function as follows:

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

Operações intercaladasInterleaved Operations

Há um problema de desempenho potencial com o uso do método WhenAny para dar suporte a um cenário de intercalação quando você está trabalhando com grandes conjuntos de tarefas.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. Todas as chamadas para WhenAny fazem uma continuação ser registrada com cada tarefa.Every call to WhenAny results in a continuation being registered with each task. Para N número de tarefas, isso resulta em o (N2) de continuação criadas durante o tempo de vida da operação de intercalação.For N number of tasks, this results in O(N2) continuations created over the lifetime of the interleaving operation. Se você estiver trabalhando com um grande conjunto de tarefas, poderá usar um combinador (Interleaved no exemplo a seguir) para resolver o problema de desempenho: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;
}

Você poderá usar o combinador para processar os resultados das tarefas conforme elas forem concluídas, por exemplo: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

Em determinados cenários de dispersão/coleta, convém esperar por todas as tarefas em um conjunto, a menos que uma delas falhe, caso em que você desejará interromper a espera assim que a exceção ocorrer.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. Você pode realizar isso com um método combinador tal como WhenAllOrFirstException no exemplo a seguir: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;
}

Compilando estruturas de dados com base em tarefaBuilding Task-based Data Structures

Além da capacidade de compilar combinadores baseados em tarefas personalizados, ter uma estrutura de dados em Task e Task<TResult>, que representa tanto os resultados de uma operação assíncrona quanto a sincronização necessária para unir a ela, torna esse um tipo muito poderoso no qual compilar estruturas de dados personalizadas a serem usados em cenários assíncronos.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

Um aspecto importante de uma tarefa é que ela pode ser enviada para vários consumidores, todos os quais podem esperar por ela, registrar continuações com ela, obter seu resultado ou exceções (no caso de Task<TResult>) e assim por diante.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. Isso torna Task e Task<TResult> perfeitamente adequado para ser usado em uma infraestrutura de cache assíncrona.This makes Task and Task<TResult> perfectly suited to be used in an asynchronous caching infrastructure. Aqui está um exemplo de um cache assíncrono pequeno mas poderoso, construído com base na 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;
        }
    }
}

A classe AsyncCache<TKey,TValue> aceita, como delegado para o seu construtor, uma função que recebe um TKey e retorna um 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>. Quaisquer valores do cache previamente acessados são armazenados no dicionário interno e AsyncCache garante que apenas uma tarefa seja gerada por chave, mesmo que o cache seja acessado simultaneamente.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.

Por exemplo, você pode compilar um cache para páginas da Web baixadas:For example, you can build a cache for downloaded web pages:

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

Você pode em seguida usar esse cache em métodos assíncronos sempre que precisar do conteúdo de uma página da Web.You can then use this cache in asynchronous methods whenever you need the contents of a web page. A classe AsyncCache garante que você esteja baixando o mínimo possível de páginas e armazena os resultados em 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["https://www.microsoft.com"];
    }
    finally { btnDownload.IsEnabled = true; }
}

AsyncProducerConsumerCollectionAsyncProducerConsumerCollection

Você também pode usar tarefas para compilar estruturas de dados para coordenar atividades assíncronas.You can also use tasks to build data structures for coordinating asynchronous activities. Considere um dos padrões de design paralelos clássicos: produtor/consumidor.Consider one of the classic parallel design patterns: producer/consumer. Nesse padrão, produtores geram dados que são consumidos pelos consumidores e produtores e consumidores podem executar em paralelo.In this pattern, producers generate data that is consumed by consumers, and the producers and consumers may run in parallel. Por exemplo, o consumidor processa o item 1, que anteriormente foi gerado por um produtor que agora está produzindo o item 2.For example, the consumer processes item 1, which was previously generated by a producer who is now producing item 2. Para o padrão de produtor/consumidor, você precisa invariavelmente que alguma estrutura de dados armazene o trabalho criado pelos produtores para que os consumidores possam ser notificados sobre novos dados e localizá-los quando disponíveis.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.

Eis aqui uma estrutura de dados simples criada sobre tarefas que permitem que os métodos assíncronos sejam usados como produtores e consumidores: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;
            }
        }
    }
}

Com essa estrutura de dados estabelecida, você pode escrever código como o seguinte: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);
}

O namespace System.Threading.Tasks.Dataflow inclui o tipo BufferBlock<T>, que pode ser usado de maneira semelhante, mas sem a necessidade de criar um tipo de coleção personalizado: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);
}

Observação

O namespace System.Threading.Tasks.Dataflow está disponível no .NET Framework 4.5 por meio de NuGet.The System.Threading.Tasks.Dataflow namespace is available in the .NET Framework 4.5 through NuGet. Para instalar o assembly que contém o namespace System.Threading.Tasks.Dataflow, abra seu projeto no Visual Studio, escolha Gerenciar Pacotes NuGet no menu Projeto e procure online pelo pacote Microsoft.Tpl.Dataflow.To install the assembly that contains the System.Threading.Tasks.Dataflow namespace, open your project in Visual Studio, choose Manage NuGet Packages from the Project menu, and search online for the Microsoft.Tpl.Dataflow package.

Consulte tambémSee also