Použití asynchronního vzoru založeného na úlozeConsuming the Task-based Asynchronous Pattern

Při použití asynchronního vzoru založeného na úlohách (klepnutím) pro práci s asynchronními operacemi můžete pomocí zpětných volání dosáhnout čekání bez blokování.When you use the Task-based Asynchronous Pattern (TAP) to work with asynchronous operations, you can use callbacks to achieve waiting without blocking. Pro úlohy se to dosáhne prostřednictvím metod, jako je Task.ContinueWith.For tasks, this is achieved through methods such as Task.ContinueWith. Jazyková asynchronní podpora skrývá zpětná volání tím, že umožňuje v normálním toku řízení očekávat asynchronní operace a kód generovaný kompilátorem poskytuje stejnou podporu na úrovni 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.

Pozastavení provádění s operátorem awaitSuspending Execution with Await

Počínaje .NET Framework 4,5 můžete použít klíčové slovo await v C# a operátor await v Visual Basic k asynchronnímu očekávání Task a Task<TResult> objektů.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. Když očekáváte Task, je výraz await typu void.When you're awaiting a Task, the await expression is of type void. Když očekáváte Task<TResult>, je výraz await typu TResult.When you're awaiting a Task<TResult>, the await expression is of type TResult. Výraz await musí být uvnitř těla asynchronní metody.An await expression must occur inside the body of an asynchronous method. Další informace o C# a Visual Basic podpoře jazyků v .NET Framework 4,5 naleznete v tématu Specifikace jazyka C# a 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.

V rámci pokrývá funkce await nainstaluje zpětné volání na úkol pomocí pokračování.Under the covers, the await functionality installs a callback on the task by using a continuation. Toto zpětné volání pokračuje asynchronní metodou v okamžiku pozastavení.This callback resumes the asynchronous method at the point of suspension. Pokud je asynchronní metoda obnovena, pokud se očekávaná operace úspěšně dokončila a byla Task<TResult>, vrátí se TResult.When the asynchronous method is resumed, if the awaited operation completed successfully and was a Task<TResult>, its TResult is returned. Pokud Task nebo Task<TResult>, která byla očekávána jako ukončená ve stavu Canceled, je vyvolána výjimka OperationCanceledException.If the Task or Task<TResult> that was awaited ended in the Canceled state, an OperationCanceledException exception is thrown. Pokud Task nebo Task<TResult>, které byly očekávány ve stavu Faulted, je vyvolána výjimka, která způsobila chybu.If the Task or Task<TResult> that was awaited ended in the Faulted state, the exception that caused it to fault is thrown. Task může způsobit chybu v důsledku několika výjimek, ale šíří se jenom jedna z těchto výjimek.A Task can fault as a result of multiple exceptions, but only one of these exceptions is propagated. Nicméně vlastnost Task.Exception vrací výjimku AggregateException, která obsahuje všechny chyby.However, the Task.Exception property returns an AggregateException exception that contains all the errors.

Pokud je kontext synchronizace (objektSynchronizationContext) spojen s vláknem, které provádělo asynchronní metodu v době pozastavení (například pokud vlastnost SynchronizationContext.Current není null), asynchronní metoda pokračuje na stejném místě. kontext synchronizace pomocí metody Post kontextu.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. V opačném případě se spoléhá na Plánovač úloh (TaskScheduler objekt), který byl aktuální v době pozastavení.Otherwise, it relies on the task scheduler (TaskScheduler object) that was current at the time of suspension. Obvykle se jedná o výchozí Plánovač úloh (TaskScheduler.Default), který cílí na fond vláken.Typically, this is the default task scheduler (TaskScheduler.Default), which targets the thread pool. Tento Plánovač úloh určuje, zda má očekávaná asynchronní operace pokračovat v místě, kde byla dokončena, nebo zda má být obnovení plánováno.This task scheduler determines whether the awaited asynchronous operation should resume where it completed or whether the resumption should be scheduled. Výchozí Plánovač obvykle umožňuje spuštění pokračování ve vlákně, které čekalé operace dokončila.The default scheduler typically allows the continuation to run on the thread that the awaited operation completed.

Když je volána asynchronní metoda, synchronně spustí tělo funkce až do prvního výrazu await v neočekávané instanci, která ještě nebyla dokončena, v tomto okamžiku se volání vrátí volajícímu.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. Pokud asynchronní metoda nevrací void, je vrácen objekt Task nebo Task<TResult>, který představuje průběžné výpočty.If the asynchronous method does not return void, a Task or Task<TResult> object is returned to represent the ongoing computation. V asynchronní metodě, která není typu void, je-li k disřádku návratový příkaz nebo je dosaženo konce textu metody, je úloha dokončena v RanToCompletion konečném stavu.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. Pokud Neošetřená výjimka způsobí, že ovládací prvek opustí tělo asynchronní metody, úloha skončí ve stavu Faulted.If an unhandled exception causes control to leave the body of the asynchronous method, the task ends in the Faulted state. Pokud je tato výjimka OperationCanceledException, úloha se místo toho ukončí ve stavu Canceled.If that exception is an OperationCanceledException, the task instead ends in the Canceled state. Tímto způsobem je výsledek nebo výjimka nakonec publikována.In this manner, the result or exception is eventually published.

Existuje několik důležitých variant tohoto chování.There are several important variations of this behavior. Z důvodu výkonu, pokud úloha již byla dokončena v době, kdy je úkol očekáván, řízení není získáno a funkce bude pokračovat v provádění.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. Navíc návrat do původního kontextu není vždy požadovaným chováním a lze jej změnit. Tato informace je podrobněji popsána v následující části.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.

Konfigurace pozastavení a obnovení s využitím yield a ConfigureAwaitConfiguring Suspension and Resumption with Yield and ConfigureAwait

Několik metod poskytuje větší kontrolu nad prováděním asynchronní metody.Several methods provide more control over an asynchronous method’s execution. Například můžete použít metodu Task.Yield k zavedení bodu kluzu do asynchronní metody: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();
    …
}

Jedná se o ekvivalent asynchronního účtování nebo plánování zpět do aktuálního kontextu.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
        ...
    }
});

Můžete také použít metodu Task.ConfigureAwait pro lepší kontrolu nad přerušením a pokračováním v asynchronní metodě.You can also use the Task.ConfigureAwait method for better control over suspension and resumption in an asynchronous method. Jak bylo zmíněno dříve, aktuální kontext je zachycen v době pozastavení asynchronní metody a tento zachycený kontext slouží k vyvolání pokračování asynchronní metody při obnovení.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. V mnoha případech se jedná o přesné chování, které požadujete.In many cases, this is the exact behavior you want. V jiných případech se může stát, že nebudete mít pozor na kontext pokračování a můžete dosáhnout lepšího výkonu tím, že tyto příspěvky vyloučíte zpět do původního kontextu.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. Chcete-li to povolit, použijte metodu Task.ConfigureAwait k informování operace await, která nezachycuje a obnovila kontext, ale pro pokračování v provádění bez ohledu na asynchronní operaci, která byla očekávána: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);

Zrušení asynchronní operaceCanceling an Asynchronous Operation

Počínaje .NET Framework 4 klepněte na metody, které podporují zrušení, poskytují alespoň jedno přetížení, které přijímá token zrušení (objektCancellationToken).Starting with the .NET Framework 4, TAP methods that support cancellation provide at least one overload that accepts a cancellation token (CancellationToken object).

Token zrušení se vytvoří prostřednictvím zdroje tokenu zrušení (CancellationTokenSource objekt).A cancellation token is created through a cancellation token source (CancellationTokenSource object). Vlastnost Token zdroje vrátí token zrušení, který bude signalizovaná při volání metody zdroje Cancel.The source’s Token property returns the cancellation token that will be signaled when the source’s Cancel method is called. Například pokud chcete stáhnout jednu webovou stránku a chcete mít možnost operaci zrušit, vytvoříte objekt CancellationTokenSource, předáte jeho token do metody klepnutí a potom zavoláte metodu zdrojového Cancel, až budete připraveni operaci zrušit. :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();

Chcete-li zrušit více asynchronních volání, můžete stejný token předat všem voláním: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();

Nebo můžete stejný token předat do selektivní podmnožiny operací: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();

Žádosti o zrušení můžou být iniciované z libovolného vlákna.Cancellation requests may be initiated from any thread.

Hodnotu CancellationToken.None můžete předat libovolné metodě, která přijímá token zrušení, což znamená, že zrušení nebude nikdy požadováno.You can pass the CancellationToken.None value to any method that accepts a cancellation token to indicate that cancellation will never be requested. Tím dojde k tomu, že vlastnost CancellationToken.CanBeCanceled vrátí falsea volaná metoda je odpovídajícím způsobem optimalizovat.This causes the CancellationToken.CanBeCanceled property to return false, and the called method can optimize accordingly. Pro účely testování můžete také předat předem zrušený token zrušení, jehož instance je vytvořena pomocí konstruktoru, který přijímá logickou hodnotu, k určení, zda má být token začínat již zrušeným nebo nestornovaným stavem.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.

Tento přístup k zrušení má několik výhod:This approach to cancellation has several advantages:

  • Stejný token zrušení můžete předat libovolnému počtu asynchronních a synchronních operací.You can pass the same cancellation token to any number of asynchronous and synchronous operations.

  • Stejná žádost o zrušení může být předaná na libovolný počet posluchačů.The same cancellation request may be proliferated to any number of listeners.

  • Vývojář asynchronního rozhraní API má úplnou kontrolu nad tím, zda může být zrušení požadováno a kdy se může projevit.The developer of the asynchronous API is in complete control of whether cancellation may be requested and when it may take effect.

  • Kód, který využívá rozhraní API, může selektivně určit asynchronní vyvolání, na které se požadavky na zrušení rozšíří.The code that consumes the API may selectively determine the asynchronous invocations that cancellation requests will be propagated to.

Sledování průběhuMonitoring Progress

Některé asynchronní metody zpřístupňují průběh prostřednictvím rozhraní průběhu předaného do asynchronní metody.Some asynchronous methods expose progress through a progress interface passed into the asynchronous method. Představte si například funkci, která asynchronně stáhne textový řetězec a společně způsob vyvolává aktualizace průběhu, které zahrnují procento stahování, které bylo doposud dokončeno.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. Taková metoda by mohla být spotřebována v aplikaci Windows Presentation Foundation (WPF) následujícím způsobem: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; }
}

Použití integrovaného kombinátory založeného na úloháchUsing the Built-in Task-based Combinators

Obor názvů System.Threading.Tasks obsahuje několik metod pro vytváření a práci s úkoly.The System.Threading.Tasks namespace includes several methods for composing and working with tasks.

Task. RunTask.Run

Třída Task obsahuje několik Run metod, které umožňují snadno přesměrovat práci jako Task nebo Task<TResult> do fondu vláken, například: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;
    });
}

Některé z těchto Run metod, jako je například Task.Run(Func<Task>) přetížení, existují jako zkratky pro metodu TaskFactory.StartNew.Some of these Run methods, such as the Task.Run(Func<Task>) overload, exist as shorthand for the TaskFactory.StartNew method. Další přetížení, jako je například Task.Run(Func<Task>), umožňují použití operátoru await v rámci práce s převedenou zátěží, například: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);
    });
}

Taková přetížení jsou logicky ekvivalentní použití metody TaskFactory.StartNew ve spojení s metodou rozšíření Unwrap v 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

Použijte metodu FromResult ve scénářích, kde již mohou být data k dispozici a stačí je vrátit z metody vracené úlohou do 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

Použijte metodu WhenAll k asynchronnímu čekání na více asynchronních operací, které jsou reprezentovány jako úkoly.Use the WhenAll method to asynchronously wait on multiple asynchronous operations that are represented as tasks. Metoda má více přetížení, které podporují sadu neobecných úkolů nebo nejednotnou sadu obecných úloh (například asynchronní čekání na více operací vracejících anulování nebo asynchronní čekání na více metod vracejících hodnoty, kde Každá hodnota může mít jiný typ a podporovat jednotnou sadu obecných úloh (například asynchronní čekání na více metod vracejícících se 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).

Řekněme, že chcete odesílat e-mailové zprávy několika zákazníkům.Let's say you want to send email messages to several customers. Posílání zpráv se můžete překrývat, takže nečekáte na dokončení jedné zprávy, než odešlete další.You can overlap sending the messages so you're not waiting for one message to complete before sending the next. Můžete také zjistit, kdy byly operace odeslání dokončeny a zda došlo k chybám: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);

Tento kód explicitně nezpracovává výjimky, ke kterým může dojít, ale umožňuje výjimky šířit z await na výsledný úkol z 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. Chcete-li zpracovat výjimky, můžete použít následující kód: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)
{
    ...
}

V takovém případě, pokud dojde k chybě jakékoli asynchronní operace, všechny výjimky budou konsolidovány v AggregateException výjimce, která je uložena v Task, který je vrácen z metody 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. Avšak pouze jedna z těchto výjimek je šířena klíčovým slovem await.However, only one of those exceptions is propagated by the await keyword. Pokud chcete kontrolovat všechny výjimky, můžete přepsat předchozí kód následujícím způsobem: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
    }
}

Podívejme se na příklad asynchronního stahování více souborů z webu.Let's consider an example of downloading multiple files from the web asynchronously. V tomto případě mají všechny asynchronní operace homogenní typy výsledků a je snadné získat přístup k výsledkům: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));

Můžete použít stejné techniky zpracování výjimek, které jsme probrali v předchozím scénáři vracejícím anulování: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

Metodu WhenAny lze použít k asynchronnímu čekání pouze na jednu z několika asynchronních operací reprezentovaných jako úlohy, které mají být dokončeny.You can use the WhenAny method to asynchronously wait for just one of multiple asynchronous operations represented as tasks to complete. Tato metoda slouží ke čtyřem primárním případům použití:This method serves four primary use cases:

  • Redundance: operace provede několikrát a vybere tu, která se dokončí jako první (například kontaktování více webových služeb na základě akcií, které vytvoří jeden výsledek a výběr toho, který dokončí nejrychlejší).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).

  • Prokládání: spouštění více operací a čekání na jejich dokončení, ale jejich zpracování po dokončení.Interleaving: Launching multiple operations and waiting for all of them to complete, but processing them as they complete.

  • Omezování: povolení dalších operací, které se začnou provádět jako ostatní.Throttling: Allowing additional operations to begin as others complete. Toto je rozšíření scénáře pro proplutí.This is an extension of the interleaving scenario.

  • Předčasné Bailout: například operace reprezentovaná úlohou T1 se dá seskupit do úlohy WhenAny s jiným úkolem T2 a můžete počkat na WhenAny úlohu.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. Úloha T2 by mohla představovat časový limit nebo zrušení nebo nějaký jiný signál, který způsobí dokončení úlohy WhenAny před dokončením T1.Task t2 could represent a time-out, or cancellation, or some other signal that causes the WhenAny task to complete before t1 completes.

RedundanceRedundancy

Vezměte v úvahu případ, kdy si přejete rozhodnout, jestli chcete koupit zásobu.Consider a case where you want to make a decision about whether to buy a stock. K dispozici je několik webových služeb s doporučeními pro základní zdroje, ale v závislosti na denním zatížení může být každá služba v různou dobu pomalejší.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. Pomocí metody WhenAny můžete obdržet oznámení, když se dokončí nějaká operace: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);

Na rozdíl od WhenAll, která vrací nezabalené výsledky všech úloh, které byly úspěšně dokončeny, WhenAny vrátí dokončenou úlohu.Unlike WhenAll, which returns the unwrapped results of all tasks that completed successfully, WhenAny returns the task that completed. Pokud úloha selže, je důležité, abyste věděli, že se nezdařila, a pokud je úloha úspěšná, je důležité znát, ke které úloze je vrácená hodnota přidružená.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. Proto potřebujete získat přístup k výsledku vráceného úkolu nebo ho očekávat více, jak ukazuje tento příklad.Therefore, you need to access the result of the returned task, or further await it, as this example shows.

Stejně jako u WhenAllmusí být možné vyhovět výjimkám.As with WhenAll, you have to be able to accommodate exceptions. Vzhledem k tomu, že obdržíte dokončenou úlohu zpět, můžete očekávat, že vrácená úloha bude mít přenesené chyby a patřičně je try/catch. například: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);
    }
}

Kromě toho, i když se první úkol úspěšně dokončí, můžou následné úkoly selhat.Additionally, even if a first task completes successfully, subsequent tasks may fail. V tomto okamžiku máte k dispozici několik možností pro práci s výjimkami: můžete počkat, až se všechny spuštěné úlohy dokončí, a v takovém případě můžete použít metodu WhenAll, nebo se můžete rozhodnout, že jsou všechny výjimky důležité a musí být zaprotokolovány.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. K tomu můžete použít pokračování pro příjem oznámení, když se úkoly asynchronně dokončují: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); });
}

nebo:or:

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

nebo dokonce: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);

Nakonec můžete chtít zrušit všechny zbývající operace: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);

Potřeba prokládáníInterleaving

Vezměte v úvahu případ, kdy stahujete obrázky z webu a zpracováváte jednotlivé Image (například přidáním obrázku do ovládacího prvku uživatelského rozhraní).Consider a case where you're downloading images from the web and processing each image (for example, adding the image to a UI control). Je nutné provést zpracování postupně ve vlákně uživatelského rozhraní, ale chcete stáhnout obrázky jako souběžně.You have to do the processing sequentially on the UI thread, but you want to download the images as concurrently as possible. Nechcete také přidržet obrázky do uživatelského rozhraní, dokud se všechny nestáhnou – chcete je přidat po dokončení: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{}
}

Můžete také použít přejezd na scénář, který zahrnuje výpočetně náročné zpracování na ThreadPool stažených imagí. například: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{}
}

OmezováníThrottling

Vezměte v úvahu příklad prokládání s tím rozdílem, že uživatel je stahuje, takže mnoho imagí, které je potřeba stáhnout, je nutné omezit. například chcete, aby bylo možné současně provést pouze určitý počet souborů ke stažení.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. K tomuto účelu můžete spustit podmnožinu asynchronních operací.To achieve this, you can start a subset of the asynchronous operations. Po dokončení operací můžete spustit další operace, které zabírají místo: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++;
    }
}

Předčasné BailoutEarly Bailout

Vezměte v úvahu, že budete čekat asynchronně, než se operace dokončí, a současně reagovat na požadavek na zrušení uživatele (například uživatel kliknul na tlačítko Storno).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). Tento scénář je znázorněný v následujícím kódu: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;
}

Tato implementace znovu povolí uživatelské rozhraní hned po rozhodnutí Bail, ale neruší základní asynchronní operace.This implementation re-enables the user interface as soon as you decide to bail out, but doesn't cancel the underlying asynchronous operations. Další možností je zrušit nedokončené operace, když se rozhodnete Bail, ale nebudete moci znovu vytvořit uživatelské rozhraní, dokud se operace neprojeví, potenciálně v důsledku ukončení v důsledku žádosti o zrušení: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; }
}

Dalším příkladem předčasného bailoutu je použití metody WhenAny ve spojení s metodou Delay, jak je popsáno v další části.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

Můžete použít metodu Task.Delay k zavedení pozastavení do provádění asynchronní metody.You can use the Task.Delay method to introduce pauses into an asynchronous method’s execution. To je užitečné pro mnoho druhů funkcí, včetně vytváření smyček cyklického dotazování a zpoždění manipulace s uživatelským vstupem pro předem stanovenou dobu.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. Metoda Task.Delay může být také užitečná v kombinaci s Task.WhenAny pro implementaci časových limitů při čekání.The Task.Delay method can also be useful in combination with Task.WhenAny for implementing time-outs on awaits.

Pokud úkol, který je součástí větší asynchronní operace (například webové služby ASP.NET), trvá příliš dlouho, může to být způsobeno tím, že by celková operace byla neúspěšná, zejména v případě, že se nepovede dřív.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. Z tohoto důvodu je důležité mít při čekání na asynchronní operaci časový limit.For this reason, it’s important to be able to time out when waiting on an asynchronous operation. Synchronní metody Task.Wait, Task.WaitAlla Task.WaitAny přijímají hodnoty časového limitu, ale odpovídající TaskFactory.ContinueWhenAll/Task.WhenAny a výše zmíněné Task.WhenAll/metody ne.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. Místo toho můžete použít Task.Delay a Task.WhenAny v kombinaci k implementaci časového limitu.Instead, you can use Task.Delay and Task.WhenAny in combination to implement a time-out.

Například v aplikaci uživatelského rozhraní řekněme, že chcete stáhnout image a zakázat uživatelské rozhraní při stahování image.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. Pokud ale stahování trvá příliš dlouho, budete chtít znovu povolit uživatelské rozhraní a zahodit stahování: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; }
}

Totéž platí pro více souborů ke stažení, protože WhenAll vrátí úlohu: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; }
}

Sestavování kombinátory založených na úloháchBuilding Task-based Combinators

Vzhledem k tomu, že úloha může zcela představovat asynchronní operaci a poskytovat synchronní a asynchronní možnosti pro připojení k operaci, načítání výsledků a tak dále, můžete vytvářet užitečné knihovny kombinátory, které vytvářejí úkoly. Sestavujte větší vzory.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. Jak je popsáno v předchozí části, .NET Framework obsahuje několik integrovaných kombinátory, ale můžete si také vytvořit vlastní.As discussed in the previous section, the .NET Framework includes several built-in combinators, but you can also build your own. Následující části obsahují několik příkladů potenciálních metod a typů kombinátorem.The following sections provide several examples of potential combinator methods and types.

RetryOnFaultRetryOnFault

V mnoha situacích můžete zkusit operaci zopakovat, pokud se předchozí pokus nezdaří.In many situations, you may want to retry an operation if a previous attempt fails. V případě synchronního kódu můžete vytvořit pomocnou metodu, například RetryOnFault v následujícím příkladu: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);
}

Můžete vytvořit téměř identickou pomocnou metodu pro asynchronní operace, které jsou implementovány klepnutím a následně vracet úlohy: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);
}

Pak můžete použít tuto kombinátorem ke kódování opakovaných pokusů do logiky aplikace. například: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);

Dále můžete rozšířit funkci RetryOnFault.You could extend the RetryOnFault function further. Funkce například může přijmout další Func<Task>, který bude vyvolán mezi opakovanými pokusy, aby bylo možné určit, kdy se má operace opakovat. například: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);
}

Potom můžete funkci použít následujícím způsobem, aby před opakováním operace čekala na sekundu: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

V některých případech můžete využít redundanci a zlepšit latenci operace a šance na úspěch.Sometimes, you can take advantage of redundancy to improve an operation’s latency and chances for success. Vezměte v úvahu více webových služeb, které poskytují nabídky, ale v různou dobu může každá služba poskytovat různé úrovně kvality a doby odezvy.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. Pokud chcete řešit tyto výkyvy, můžete vydávat požadavky na všechny webové služby a hned po obdržení odpovědi zrušit zbývající požadavky.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. Můžete implementovat pomocnou funkci, která usnadňuje implementaci tohoto běžného vzoru spouštění více operací, čekání na jakékoli a následné zrušení zbytku.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. Funkce NeedOnlyOne v následujícím příkladu znázorňuje tento scénář: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;
}

Tuto funkci pak můžete použít následujícím způsobem:You can then use this function as follows:

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

Prokládané operaceInterleaved Operations

Při práci s velmi velkým počtem úloh je možné využít potenciální potíže s výkonem při použití metody WhenAny k podpoře scénáře prokládaných dat.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. Každé volání WhenAny má za následek pokračování zaregistrované u každého úkolu.Every call to WhenAny results in a continuation being registered with each task. Pro N počet úkolů to vede k pokračování v počtu (N2), které bylo vytvořeno během doby trvání prokládání operací.For N number of tasks, this results in O(N2) continuations created over the lifetime of the interleaving operation. Pokud pracujete s velkou sadou úkolů, můžete k vyřešení problému s výkonem použít kombinátorem (Interleaved v následujícím příkladu):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;
}

Pak můžete použít kombinátorem ke zpracování výsledků úloh po jejich dokončení. například: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

V určitých bodových nebo sběrných scénářích můžete chtít počkat na všechny úlohy v sadě, pokud jedna z nich selže, a v takovém případě se chcete přestat čekat, jakmile dojde k výjimce.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. To lze provést pomocí metody kombinátorem, jako je například WhenAllOrFirstException v následujícím příkladu: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;
}

Vytváření datových struktur založených na úloháchBuilding Task-based Data Structures

Kromě možnosti vytvářet vlastní kombinátory založený na úlohách, které mají datovou strukturu v Task a Task<TResult>, která představuje jak výsledky asynchronní operace, tak i nutná synchronizace pro připojení, díky tomu má velmi účinný typ který umožňuje vytvářet vlastní datové struktury, které se mají použít v asynchronních scénářích.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

Jedním z důležitých aspektů úkolu je, že může být předána více příjemcům, a to vše, co může očekávat, zaregistrovat pokračování s ním, získat výsledek nebo výjimky (v případě Task<TResult>) atd.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. Díky tomu jsou Task a Task<TResult> naprosto vhodné k použití v asynchronní infrastruktuře ukládání do mezipaměti.This makes Task and Task<TResult> perfectly suited to be used in an asynchronous caching infrastructure. Tady je příklad malé, ale výkonné asynchronní mezipaměti postavené nad 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;
        }
    }
}

AsyncCache<TKey třída TValue > přijímá jako delegáta funkci, která přijímá TKey a vrací 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>. Všechny dříve použité hodnoty z mezipaměti se ukládají do interního slovníku a AsyncCache zajistí, že se pro každý klíč vygeneruje jenom jeden úkol, i když se k mezipaměti přistupoval souběžně.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.

Můžete například sestavit mezipaměť pro stažené webové stránky:For example, you can build a cache for downloaded web pages:

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

Tuto mezipaměť pak můžete použít v asynchronních metodách vždy, když potřebujete obsah webové stránky.You can then use this cache in asynchronous methods whenever you need the contents of a web page. Třída AsyncCache zajišťuje, že budete stahovat co nejvíce stránek a ukládá výsledky do mezipaměti.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

Úkoly můžete použít také k vytváření datových struktur pro koordinaci asynchronních aktivit.You can also use tasks to build data structures for coordinating asynchronous activities. Vezměte v úvahu jeden z klasických paralelních vzorů návrhu: producent/příjemce.Consider one of the classic parallel design patterns: producer/consumer. V tomto vzoru producenti generují data, která jsou využívána příjemci, a producenti a spotřebitelé můžou běžet paralelně.In this pattern, producers generate data that is consumed by consumers, and the producers and consumers may run in parallel. Například příjemce zpracuje položku 1, která byla dříve vygenerována výrobcem, který nyní vyrábí položku 2.For example, the consumer processes item 1, which was previously generated by a producer who is now producing item 2. Pro vzorek producent/příjemce invariably potřebovat určitou datovou strukturu pro ukládání práce vytvořené producenty, aby se příjemci mohli dostat k oznámením o nových datech a najít je, když jsou k dispozici.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.

Tady je jednoduchá datová struktura postavená na úkolech, která umožňuje použití asynchronních metod jako výrobců a spotřebitelů: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;
            }
        }
    }
}

Pomocí této struktury dat můžete napsat kód, například následující: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);
}

Obor názvů System.Threading.Tasks.Dataflow zahrnuje typ BufferBlock<T>, který lze použít podobným způsobem, ale bez nutnosti sestavení vlastního typu kolekce: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);
}

Poznámka

Obor názvů System.Threading.Tasks.Dataflow je k dispozici v .NET Framework 4,5 až NuGet.The System.Threading.Tasks.Dataflow namespace is available in the .NET Framework 4.5 through NuGet. Chcete-li nainstalovat sestavení, které obsahuje obor názvů System.Threading.Tasks.Dataflow, otevřete projekt v aplikaci Visual Studio, v nabídce projekt vyberte možnost Spravovat balíčky NuGet a vyhledejte balíček Microsoft. tpl. Dataflow v online režimu.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.

Viz také:See also