Zrušení v knihovně PPL
Tento dokument vysvětluje roli zrušení v knihovně PPL (Parallel Patterns Library), jak zrušit paralelní práci a jak určit, kdy je paralelní práce zrušena.
Poznámka:
Modul runtime používá zpracování výjimek k implementaci zrušení. Tyto výjimky v kódu nezachytávejte ani nezpracovávejte. Kromě toho doporučujeme napsat kód bezpečný pro výjimky v těle funkcí pro vaše úkoly. Můžete například použít model inicializace (RAII) získání prostředků k zajištění správného zpracování prostředků při vyvolání výjimky v těle úlohy. Úplný příklad, který používá vzor RAII k vyčištění prostředku v zrušitelné úloze, naleznete v části Návod: Odebrání práce z vlákna uživatelského rozhraní.
Klíčové body
Zrušení je spolupráce a zahrnuje koordinaci mezi kódem, který požaduje zrušení, a úkolem, který reaguje na zrušení.
Pokud je to možné, zrušte práci pomocí tokenů zrušení. Třída concurrency::cancellation_token definuje token zrušení.
Při použití tokenů zrušení použijte metodu concurrency::cancellation_token_source::cancel k zahájení zrušení a funkci concurrency::cancel_current_task k reakci na zrušení. Pomocí metody concurrency::cancellation_token::is_canceled zkontrolujte, jestli některý jiný úkol požádal o zrušení.
Zrušení se neprojeví okamžitě. I když nová práce není spuštěna, pokud je úkol nebo skupina úkolů zrušena, aktivní práce musí zkontrolovat zrušení a reagovat na ně.
Pokračování založené na hodnotě dědí token zrušení jeho předefinované úlohy. Pokračování založené na úkolu nikdy nezdědí token jeho předefinovaného úkolu.
Při volání konstruktoru nebo funkce, která přebírá
cancellation_token
objekt, ale nechcete operaci zrušit, použijte metodu concurrency::cancellation_token::none. Pokud také nepředáte token zrušení do konstruktoru concurrency::task nebo funkce concurrency::create_task , nebude možné tuto úlohu zrušit.
V tomto dokumentu
Stromy paralelní práce
PPL používá úlohy a skupiny úkolů ke správě jemně odstupňovaných úkolů a výpočtů. Skupiny úkolů můžete vnořit a vytvořit stromy paralelní práce. Následující obrázek znázorňuje paralelní pracovní strom. Na tomto obrázku tg1
a tg2
představují skupiny úkolů; t1
, t2
t3
, , t4
a t5
představují práci, kterou skupiny úkolů provádějí.
Následující příklad ukazuje kód, který je nutný k vytvoření stromu na obrázku. V tomto příkladu tg1
jsou tg2
objekty concurrency::structured_task_group ; t1
, t2
, t3
, t4
a t5
jsou concurrency::task_handle objekty.
// task-tree.cpp
// compile with: /c /EHsc
#include <ppl.h>
#include <sstream>
#include <iostream>
#include <sstream>
using namespace concurrency;
using namespace std;
void create_task_tree()
{
// Create a task group that serves as the root of the tree.
structured_task_group tg1;
// Create a task that contains a nested task group.
auto t1 = make_task([&] {
structured_task_group tg2;
// Create a child task.
auto t4 = make_task([&] {
// TODO: Perform work here.
});
// Create a child task.
auto t5 = make_task([&] {
// TODO: Perform work here.
});
// Run the child tasks and wait for them to finish.
tg2.run(t4);
tg2.run(t5);
tg2.wait();
});
// Create a child task.
auto t2 = make_task([&] {
// TODO: Perform work here.
});
// Create a child task.
auto t3 = make_task([&] {
// TODO: Perform work here.
});
// Run the child tasks and wait for them to finish.
tg1.run(t1);
tg1.run(t2);
tg1.run(t3);
tg1.wait();
}
K vytvoření podobného pracovního stromu můžete použít také třídu concurrency::task_group . Třída concurrency::task také podporuje pojem stromu práce. task
Strom je však strom závislostí. task
Ve stromu se budoucí práce dokončí po aktuální práci. Ve stromu skupiny úkolů se vnitřní práce dokončí před vnější prací. Další informace o rozdílech mezi úkoly a skupinami úkolů naleznete v tématu Paralelismus úkolu.
[Nahoře]
Zrušení paralelních úloh
Paralelní práci můžete zrušit několika způsoby. Upřednostňovaným způsobem je použití tokenu zrušení. Skupiny úloh také podporují metodu concurrency::task_group::cancel a metodu concurrency::structured_task_group::cancel . Posledním způsobem je vyvolání výjimky v textu pracovní funkce úkolu. Bez ohledu na to, kterou metodu zvolíte, chápete, že zrušení nedojde okamžitě. I když nová práce není spuštěna, pokud je úkol nebo skupina úkolů zrušena, aktivní práce musí zkontrolovat zrušení a reagovat na ně.
Další příklady, které ruší paralelní úlohy, najdete v tématu Návod: Připojení pomocí úloh a požadavků HTTP XML, Postupy: Použití zrušení přerušení z paralelní smyčky a Postupy: Použití zpracování výjimek k přerušení paralelní smyčky.
Zrušení paralelní práce pomocí tokenu zrušení
Třídy task
a , task_group
podporují structured_task_group
zrušení prostřednictvím použití tokenů zrušení. PPL definuje třídy concurrency::cancellation_token_source a concurrency::cancellation_token pro tento účel. Pokud ke zrušení práce použijete token zrušení, modul runtime nespustí novou práci, která se přihlásí k odběru daného tokenu. Práce, která je již aktivní, může pomocí is_canceled členské funkce monitorovat token zrušení a zastavit, když může.
Chcete-li zahájit zrušení, zavolejte metodu concurrency::cancellation_token_source::cancel . Zrušení odpovíte těmito způsoby:
Pro
task
objekty použijte funkci concurrency::cancel_current_task .cancel_current_task
zruší aktuální úkol a jakékoli pokračování založené na hodnotě. (Nezruší token zrušení přidružený k úloze nebo jeho pokračování.)U skupin úloh a paralelních algoritmů použijte funkci concurrency::is_current_task_group_canceling k detekci zrušení a vrácení co nejdříve z těla úkolu, když tato funkce vrátí
true
. (Nevolejtecancel_current_task
ze skupiny úkolů.)
Následující příklad ukazuje první základní vzor pro zrušení úlohy. Tělo úlohy občas kontroluje zrušení uvnitř smyčky.
// task-basic-cancellation.cpp
// compile with: /EHsc
#include <ppltasks.h>
#include <concrt.h>
#include <iostream>
#include <sstream>
using namespace concurrency;
using namespace std;
bool do_work()
{
// Simulate work.
wcout << L"Performing work..." << endl;
wait(250);
return true;
}
int wmain()
{
cancellation_token_source cts;
auto token = cts.get_token();
wcout << L"Creating task..." << endl;
// Create a task that performs work until it is canceled.
auto t = create_task([&]
{
bool moreToDo = true;
while (moreToDo)
{
// Check for cancellation.
if (token.is_canceled())
{
// TODO: Perform any necessary cleanup here...
// Cancel the current task.
cancel_current_task();
}
else
{
// Perform work.
moreToDo = do_work();
}
}
}, token);
// Wait for one second and then cancel the task.
wait(1000);
wcout << L"Canceling task..." << endl;
cts.cancel();
// Wait for the task to cancel.
wcout << L"Waiting for task to complete..." << endl;
t.wait();
wcout << L"Done." << endl;
}
/* Sample output:
Creating task...
Performing work...
Performing work...
Performing work...
Performing work...
Canceling task...
Waiting for task to complete...
Done.
*/
Funkce cancel_current_task
se vyvolá, proto není nutné explicitně vracet z aktuální smyčky nebo funkce.
Tip
Alternativně můžete místo funkce souběžnosti volat funkci cancel_current_task
concurrency::interruption_point .
Při odpovídání na zrušení je důležité volat cancel_current_task
, protože úkol přepíná do zrušeného stavu. Pokud se místo volání cancel_current_task
vrátíte včas, operace přejde do dokončeného stavu a spustí se pokračování založená na hodnotách.
Upozornění
Nikdy z kódu nevyhazujte task_canceled
. Místo toho zavolejte cancel_current_task
.
Když úloha skončí ve zrušeném stavu, souběžnost::task::get metoda vyvolá souběžnost::task_canceled. (Naopak souběžnost::task::wait vrátí task_status::canceled a nevyvolá chybu.) Následující příklad ukazuje toto chování pro pokračování na základě úlohy. Pokračování založené na úkolech se vždy volá, i když je zrušena úloha s tecedentem.
// task-canceled.cpp
// compile with: /EHsc
#include <ppltasks.h>
#include <iostream>
using namespace concurrency;
using namespace std;
int wmain()
{
auto t1 = create_task([]() -> int
{
// Cancel the task.
cancel_current_task();
});
// Create a continuation that retrieves the value from the previous.
auto t2 = t1.then([](task<int> t)
{
try
{
int n = t.get();
wcout << L"The previous task returned " << n << L'.' << endl;
}
catch (const task_canceled& e)
{
wcout << L"The previous task was canceled." << endl;
}
});
// Wait for all tasks to complete.
t2.wait();
}
/* Output:
The previous task was canceled.
*/
Vzhledem k tomu, že pokračování založená na hodnotách dědí token své úlohy s tecedentem, pokud nebyly vytvořeny s explicitním tokenem, pokračování okamžitě vstoupí do zrušeného stavu i v případě, že se stále provádí úloha stecedent. Proto se každá výjimka, která je vyvolána úlohou stecedent po zrušení, není rozšířena na úkoly pokračování. Zrušení vždy přepíše stav úlohy typu antecedent. Následující příklad se podobá předchozímu, ale ilustruje chování pokračování založeného na hodnotě.
auto t1 = create_task([]() -> int
{
// Cancel the task.
cancel_current_task();
});
// Create a continuation that retrieves the value from the previous.
auto t2 = t1.then([](int n)
{
wcout << L"The previous task returned " << n << L'.' << endl;
});
try
{
// Wait for all tasks to complete.
t2.get();
}
catch (const task_canceled& e)
{
wcout << L"The task was canceled." << endl;
}
/* Output:
The task was canceled.
*/
Upozornění
Pokud konstruktoru task
nebo funkci concurrency::create_task nepředáte token zrušení, nebude možné tuto úlohu zrušit. Kromě toho musíte předat stejný token zrušení konstruktoru všech vnořených úkolů (tj. úkolů vytvořených v těle jiného úkolu), aby se všechny úkoly zrušily současně.
Při zrušení tokenu zrušení můžete chtít spustit libovolný kód. Pokud například uživatel zvolí tlačítko Storno v uživatelském rozhraní pro zrušení operace, můžete toto tlačítko zakázat, dokud uživatel nespustí jinou operaci. Následující příklad ukazuje, jak pomocí metody concurrency::cancellation_token::register_callback zaregistrovat funkci zpětného volání, která se spustí při zrušení tokenu zrušení.
// task-cancellation-callback.cpp
// compile with: /EHsc
#include <ppltasks.h>
#include <iostream>
using namespace concurrency;
using namespace std;
int wmain()
{
cancellation_token_source cts;
auto token = cts.get_token();
// An event that is set in the cancellation callback.
event e;
cancellation_token_registration cookie;
cookie = token.register_callback([&e, token, &cookie]()
{
wcout << L"In cancellation callback..." << endl;
e.set();
// Although not required, demonstrate how to unregister
// the callback.
token.deregister_callback(cookie);
});
wcout << L"Creating task..." << endl;
// Create a task that waits to be canceled.
auto t = create_task([&e]
{
e.wait();
}, token);
// Cancel the task.
wcout << L"Canceling task..." << endl;
cts.cancel();
// Wait for the task to cancel.
t.wait();
wcout << L"Done." << endl;
}
/* Sample output:
Creating task...
Canceling task...
In cancellation callback...
Done.
*/
Dokument Paralelismus úkolů vysvětluje rozdíl mezi pokračováními založenými na hodnotách a úloh. Pokud nezadáte cancellation_token
objekt úkolu pokračování, pokračování zdědí token zrušení z úlohy antecedent následujícími způsoby:
Pokračování na základě hodnoty vždy dědí token zrušení úlohy s tecedent.
Pokračování založené na úkolu nikdy nedědí token zrušení úlohy s tecedent. Jediným způsobem, jak zrušit pokračování na základě úlohy, je explicitně předat token zrušení.
Toto chování není ovlivněno chybnou úlohou (to znamená tím, že vyvolá výjimku). V tomto případě je zrušena pokračování na základě hodnoty; pokračování na základě úlohy se nezruší.
Upozornění
Úkol vytvořený v jiném úkolu (jinými slovy vnořený úkol) nedědí token zrušení nadřazeného úkolu. Pouze pokračování založené na hodnotě dědí token zrušení jeho předefinované úlohy.
Tip
Při volání konstruktoru nebo funkce, která přebírá cancellation_token
objekt, použijte metodu concurrency::cancellation_token::none a nechcete, aby operace byla zrušena.
Token zrušení můžete také poskytnout konstruktoru objektu task_group
nebo structured_task_group
objektu. Důležitým aspektem je, že podřízené skupiny úloh dědí tento token zrušení. Příklad, který ukazuje tento koncept pomocí funkce concurrency::run_with_cancellation_token ke spuštění volání parallel_for
, viz Zrušení paralelních algoritmů dále v tomto dokumentu.
[Nahoře]
Tokeny zrušení a skládání úloh
Funkce concurrency::when_all a concurrency::when_any vám můžou pomoct vytvořit několik úloh pro implementaci běžných vzorů. Tato část popisuje, jak tyto funkce fungují s tokeny zrušení.
Když funkci poskytnete token when_all
when_any
zrušení, zruší se tato funkce pouze v případě, že dojde ke zrušení tokenu zrušení nebo když některý z úkolů účastníka skončí ve zrušeném stavu nebo vyvolá výjimku.
Funkce when_all
dědí token zrušení z každé úlohy, která kompozuje celkovou operaci, když mu nezadáte token zrušení. Úloha vrácená z when_all
tohoto tokenu se zruší, když dojde ke zrušení některého z těchto tokenů a alespoň jeden z úkolů účastníka ještě nezačal nebo je spuštěný. Podobné chování nastane, když některý z úkolů vyvolá výjimku – úloha vrácená z when_all
této výjimky se okamžitě zruší.
Modul runtime zvolí token zrušení pro úlohu, která je vrácena z when_any
funkce po dokončení úlohy. Pokud žádný z úkolů účastníka nedokončí v dokončeném stavu a jeden nebo více úkolů vyvolá výjimku, je vybrán jeden z úkolů, které hodili dokončení when_any
a jeho token je zvolen jako token pro konečný úkol. Pokud se v dokončeném stavu dokončí více než jeden úkol, skončí úkol vrácený z when_any
úkolu v dokončeném stavu. Modul runtime se pokusí vybrat dokončenou úlohu, jejíž token není zrušen v době dokončení, aby úloha vrácená z when_any
ní nebyla okamžitě zrušena, i když ostatní spuštěné úkoly mohou být dokončeny později.
[Nahoře]
Použití metody cancel ke zrušení paralelní práce
Souběžnost ::task_group::cancel a concurrency::structured_task_group::cancel metody nastaví skupinu úloh na zrušený stav. Po volání cancel
skupina úkolů nespustí budoucí úkoly. Metody cancel
lze volat více podřízenými úkoly. Zrušený úkol způsobí , že souběžnost::task_group::wait a concurrency:::structured_task_group::wait metody vrátí souběžnost::canceled.
Pokud je skupina úloh zrušena, volání z každého podřízeného úkolu do modulu runtime může aktivovat bod přerušení, což způsobí, že modul runtime vyvolá a zachytí interní typ výjimky, aby zrušil aktivní úlohy. Modul Concurrency Runtime nedefinuje konkrétní body přerušení; mohou nastat v jakémkoli volání modulu runtime. Modul runtime musí zpracovat výjimky, které vyvolá, aby bylo možné provést zrušení. Proto nezpracovávejte neznámé výjimky v textu úkolu.
Pokud podřízená úloha provádí časově náročnou operaci a nevolá do modulu runtime, musí pravidelně kontrolovat zrušení a ukončení včas. Následující příklad ukazuje jeden ze způsobů, jak určit, kdy je práce zrušena. Úkol t4
zruší nadřazenou skupinu úloh, když dojde k chybě. Úloha t5
občas volá metodu structured_task_group::is_canceling
, která kontroluje zrušení. Pokud je nadřazená skupina úloh zrušena, úloha t5
vytiskne zprávu a ukončí se.
structured_task_group tg2;
// Create a child task.
auto t4 = make_task([&] {
// Perform work in a loop.
for (int i = 0; i < 1000; ++i)
{
// Call a function to perform work.
// If the work function fails, cancel the parent task
// and break from the loop.
bool succeeded = work(i);
if (!succeeded)
{
tg2.cancel();
break;
}
}
});
// Create a child task.
auto t5 = make_task([&] {
// Perform work in a loop.
for (int i = 0; i < 1000; ++i)
{
// To reduce overhead, occasionally check for
// cancelation.
if ((i%100) == 0)
{
if (tg2.is_canceling())
{
wcout << L"The task was canceled." << endl;
break;
}
}
// TODO: Perform work here.
}
});
// Run the child tasks and wait for them to finish.
tg2.run(t4);
tg2.run(t5);
tg2.wait();
Tento příklad kontroluje zrušení každé 100. iterace smyčky úlohy. Frekvence, s jakou kontrolujete zrušení, závisí na množství práce, kterou váš úkol provádí, a na tom, jak rychle potřebujete, aby úkoly reagovaly na zrušení.
Pokud nemáte přístup k nadřazeného objektu skupiny úloh, zavolejte funkci concurrency::is_current_task_group_canceling a určete, zda je nadřazená skupina úloh zrušena.
Metoda cancel
ovlivňuje pouze podřízené úkoly. Pokud například zrušíte skupinu tg1
úkolů na obrázku paralelního pracovního stromu, ovlivní to všechny úkoly ve stromu (t1
, t2
, t3
, t4
a t5
). Pokud zrušíte vnořenou skupinu úloh, tg2
budou ovlivněny pouze úkoly t4
a t5
budou ovlivněny.
Při volání cancel
metody jsou také zrušeny všechny podřízené skupiny úloh. Zrušení však nemá vliv na žádné nadřazené členy skupiny úkolů v paralelním pracovním stromu. Následující příklady ukazují, že je třeba stavět na obrázku paralelního pracovního stromu.
První z těchto příkladů vytvoří pracovní funkci pro úkol t4
, což je podřízená skupina tg2
úkolů . Pracovní funkce volá funkci work
ve smyčce. Pokud jakékoli volání work
selže, úkol zruší svou nadřazenou skupinu úkolů. To způsobí, že skupina tg2
úkolů přejde do zrušeného stavu, ale nezruší skupinu tg1
úloh .
auto t4 = make_task([&] {
// Perform work in a loop.
for (int i = 0; i < 1000; ++i)
{
// Call a function to perform work.
// If the work function fails, cancel the parent task
// and break from the loop.
bool succeeded = work(i);
if (!succeeded)
{
tg2.cancel();
break;
}
}
});
Tento druhý příklad se podobá prvnímu, s tím rozdílem, že úkol zruší skupinu tg1
úkolů . To ovlivňuje všechny úkoly ve stromu (t1
, t2
, t3
, t4
a t5
).
auto t4 = make_task([&] {
// Perform work in a loop.
for (int i = 0; i < 1000; ++i)
{
// Call a function to perform work.
// If the work function fails, cancel all tasks in the tree.
bool succeeded = work(i);
if (!succeeded)
{
tg1.cancel();
break;
}
}
});
Třída structured_task_group
není bezpečná pro přístup z více vláken. Podřízená úloha, která volá metodu nadřazeného structured_task_group
objektu, proto vytváří nezadané chování. Výjimkou tohoto pravidla jsou structured_task_group::cancel
metody a souběžnost::structured_task_group::is_canceling . Podřízený úkol může volat tyto metody, aby zrušila nadřazenou skupinu úloh a zkontrolovala zrušení.
Upozornění
I když můžete pomocí tokenu zrušení zrušit práci prováděnou skupinou úloh, která běží jako podřízený task
objekt, nemůžete použít task_group::cancel
ani structured_task_group::cancel
metody ke zrušení task
objektů spuštěných ve skupině úloh.
[Nahoře]
Zrušení paralelní práce pomocí výjimek
Použití tokenů zrušení a cancel
metody jsou efektivnější než zpracování výjimek při rušení paralelního pracovního stromu. Tokeny zrušení a cancel
metoda zruší úlohu a všechny podřízené úkoly shora dolů. Zpracování výjimek naopak funguje směrem dolů a musí zrušit každou podřízenou skupinu úloh nezávisle, protože se výjimka šíří směrem nahoru. Téma Zpracování výjimek vysvětluje, jak Concurrency Runtime používá výjimky ke komunikaci chyb. Ne všechny výjimky však značí chybu. Vyhledávací algoritmus může například zrušit přidružený úkol, když najde výsledek. Jak už jsme ale zmínili dříve, zpracování výjimek je méně efektivní než použití cancel
metody ke zrušení paralelní práce.
Upozornění
K zrušení paralelní práce doporučujeme použít výjimky pouze v případě potřeby. Tokeny zrušení a metody skupiny cancel
úloh jsou efektivnější a méně náchylné k chybám.
Když vyvoláte výjimku v textu pracovní funkce, kterou předáte skupině úloh, modul runtime uloží tuto výjimku a zařadí výjimku do kontextu, který čeká na dokončení skupiny úloh. Stejně jako u cancel
této metody modul runtime zahodí všechny úlohy, které ještě nebyly spuštěny, a nepřijímá nové úlohy.
Tento třetí příklad se podobá druhému, s tím rozdílem, že úkol t4
vyvolá výjimku pro zrušení skupiny tg2
úloh . Tento příklad používá try
-catch
blok ke kontrole zrušení, když skupina tg2
úloh čeká na dokončení podřízených úkolů. Stejně jako v prvním příkladu to způsobí, že skupina tg2
úloh přejde do zrušeného stavu, ale nezruší skupinu tg1
úloh .
structured_task_group tg2;
// Create a child task.
auto t4 = make_task([&] {
// Perform work in a loop.
for (int i = 0; i < 1000; ++i)
{
// Call a function to perform work.
// If the work function fails, throw an exception to
// cancel the parent task.
bool succeeded = work(i);
if (!succeeded)
{
throw exception("The task failed");
}
}
});
// Create a child task.
auto t5 = make_task([&] {
// TODO: Perform work here.
});
// Run the child tasks.
tg2.run(t4);
tg2.run(t5);
// Wait for the tasks to finish. The runtime marshals any exception
// that occurs to the call to wait.
try
{
tg2.wait();
}
catch (const exception& e)
{
wcout << e.what() << endl;
}
Tento čtvrtý příklad používá zpracování výjimek ke zrušení celého pracovního stromu. Příklad zachytí výjimku, když skupina tg1
úloh čeká na dokončení podřízených úkolů místo toho, když skupina tg2
úkolů čeká na podřízené úkoly. Podobně jako v druhém příkladu to způsobí, že obě skupiny úkolů ve stromu tg1
a tg2
, vstoupí do zrušeného stavu.
// Run the child tasks.
tg1.run(t1);
tg1.run(t2);
tg1.run(t3);
// Wait for the tasks to finish. The runtime marshals any exception
// that occurs to the call to wait.
try
{
tg1.wait();
}
catch (const exception& e)
{
wcout << e.what() << endl;
}
Vzhledem k tomu, že metody task_group::wait
a structured_task_group::wait
metody vyvolá, když podřízený úkol vyvolá výjimku, neobdržíte z nich návratovou hodnotu.
[Nahoře]
Zrušení paralelních algoritmů
Paralelní algoritmy v PPL například parallel_for
vycházejí ze skupin úloh. Proto můžete použít mnoho stejných technik ke zrušení paralelního algoritmu.
Následující příklady ilustrují několik způsobů zrušení paralelního algoritmu.
Následující příklad používá run_with_cancellation_token
funkci k volání parallel_for
algoritmu. Funkce run_with_cancellation_token
přebírá token zrušení jako argument a synchronně volá zadanou pracovní funkci. Vzhledem k tomu, že paralelní algoritmy jsou založené na úkolech, dědí token zrušení nadřazené úlohy. parallel_for
Proto může reagovat na zrušení.
// cancel-parallel-for.cpp
// compile with: /EHsc
#include <ppltasks.h>
#include <iostream>
#include <sstream>
using namespace concurrency;
using namespace std;
int wmain()
{
// Call parallel_for in the context of a cancellation token.
cancellation_token_source cts;
run_with_cancellation_token([&cts]()
{
// Print values to the console in parallel.
parallel_for(0, 20, [&cts](int n)
{
// For demonstration, cancel the overall operation
// when n equals 11.
if (n == 11)
{
cts.cancel();
}
// Otherwise, print the value.
else
{
wstringstream ss;
ss << n << endl;
wcout << ss.str();
}
});
}, cts.get_token());
}
/* Sample output:
15
16
17
10
0
18
5
*/
Následující příklad používá metodu concurrency::structured_task_group::run_and_wait k volání parallel_for
algoritmu. Metoda structured_task_group::run_and_wait
čeká na dokončení zadané úlohy. Objekt structured_task_group
umožňuje pracovní funkci zrušit úlohu.
// To enable cancelation, call parallel_for in a task group.
structured_task_group tg;
task_group_status status = tg.run_and_wait([&] {
parallel_for(0, 100, [&](int i) {
// Cancel the task when i is 50.
if (i == 50)
{
tg.cancel();
}
else
{
// TODO: Perform work here.
}
});
});
// Print the task group status.
wcout << L"The task group status is: ";
switch (status)
{
case not_complete:
wcout << L"not complete." << endl;
break;
case completed:
wcout << L"completed." << endl;
break;
case canceled:
wcout << L"canceled." << endl;
break;
default:
wcout << L"unknown." << endl;
break;
}
Tento příklad vytvoří následující výstup.
The task group status is: canceled.
Následující příklad používá zpracování výjimek ke zrušení smyčky parallel_for
. Modul runtime zařadí výjimku do volajícího kontextu.
try
{
parallel_for(0, 100, [&](int i) {
// Throw an exception to cancel the task when i is 50.
if (i == 50)
{
throw i;
}
else
{
// TODO: Perform work here.
}
});
}
catch (int n)
{
wcout << L"Caught " << n << endl;
}
Tento příklad vytvoří následující výstup.
Caught 50
Následující příklad používá logický příznak ke koordinaci zrušení ve smyčce parallel_for
. Každá úloha se spustí, protože tento příklad nepoužívá metodu cancel
nebo zpracování výjimek ke zrušení celkové sady úloh. Proto může mít tato technika větší výpočetní režii než mechanismus zrušení.
// Create a Boolean flag to coordinate cancelation.
bool canceled = false;
parallel_for(0, 100, [&](int i) {
// For illustration, set the flag to cancel the task when i is 50.
if (i == 50)
{
canceled = true;
}
// Perform work if the task is not canceled.
if (!canceled)
{
// TODO: Perform work here.
}
});
Každá metoda zrušení má oproti ostatním výhodám. Zvolte metodu, která odpovídá vašim konkrétním potřebám.
[Nahoře]
Kdy nepoužívat zrušení
Použití zrušení je vhodné, když každý člen skupiny souvisejících úkolů může včas ukončit. Existují však některé scénáře, kdy zrušení nemusí být vhodné pro vaši aplikaci. Protože zrušení úkolu je například spolupracující, celková sada úkolů se nezruší, pokud je blokovaný žádný jednotlivý úkol. Pokud například jeden úkol ještě nezačal, ale odblokuje jiný aktivní úkol, nespustí se, pokud je skupina úkolů zrušena. To může způsobit zablokování ve vaší aplikaci. Druhý příklad, kdy použití zrušení nemusí být vhodné, je, když je úkol zrušen, ale jeho podřízený úkol provádí důležitou operaci, například uvolnění zdroje. Vzhledem k tomu, že se při zrušení nadřazené úlohy zruší celková sada úkolů, tato operace se nespustí. Příklad, který ukazuje tento bod, najdete v části Vysvětlení toho, jak zrušení a zpracování výjimek ovlivňuje zničení objektů v tématu Osvědčené postupy v tématu Knihovny paralelních vzorů.
[Nahoře]
Příbuzná témata
Titulek | Popis |
---|---|
Postupy: Přerušení paralelní smyčky pomocí zrušení | Ukazuje, jak použít zrušení k implementaci paralelního vyhledávacího algoritmu. |
Postupy: Přerušení paralelní smyčky pomocí zpracování výjimek | Ukazuje, jak pomocí task_group třídy napsat vyhledávací algoritmus pro základní stromovou strukturu. |
Zpracování výjimek | Popisuje, jak modul runtime zpracovává výjimky, které jsou vyvolány skupinami úloh, jednoduchými úlohami a asynchronními agenty a jak reagovat na výjimky ve vašich aplikacích. |
Paralelismus úkolu | Popisuje, jak úkoly souvisejí se skupinami úkolů a jak můžete ve svých aplikacích používat nestrukturované a strukturované úkoly. |
Paralelní algoritmy | Popisuje paralelní algoritmy, které souběžně provádějí práci na kolekcích dat. |
Knihovna PPL (Parallel Patterns Library) | Poskytuje přehled knihovny paralelních vzorů. |
Reference
task – třída (Concurrency Runtime)