Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Det här dokumentet beskriver rollen för annullering i PPL (Parallel Patterns Library), hur du avbryter parallellt arbete och hur du avgör när parallellt arbete avbryts.
Anmärkning
Körtiden använder undantagshantering för att implementera annullering. Fånga eller hantera inte dessa undantag i koden. Dessutom rekommenderar vi att du skriver undantagssäker kod i funktionsorganen för dina uppgifter. Du kan till exempel använda mönstret Resource Acquisition Is Initialization (RAII) för att säkerställa att resurser hanteras korrekt när ett undantag kastas i en uppgiftskropp. Ett fullständigt exempel som använder RAII-mönstret för att rensa en resurs i en avbruten uppgift finns i Genomgång: Ta bort arbete från en User-Interface tråd.
Huvudpunkter
Annulleringen är samarbetsinriktad och omfattar samordning mellan den kod som begär annullering och den uppgift som svarar på annulleringen.
Använd annulleringstoken när det är möjligt för att avbryta arbetet. Klassen concurrency::cancellation_token definierar en avbrottstoken.
När du använder annulleringstoken bör du använda metoden concurrency::cancellation_token_source::cancel för att initiera annullering och funktionen concurrency::cancel_current_task för att svara på annulleringen. Använd metoden concurrency::cancellation_token::is_canceled för att kontrollera om någon annan aktivitet har begärt annullering.
Annullering sker inte omedelbart. Även om nytt arbete inte startas om en aktivitet eller aktivitetsgrupp avbryts, måste aktivt arbete söka efter och svara på annulleringen.
En värdebaserad fortsättningsuppgift ärver annulleringstoken från sin föregående uppgift. En uppgiftsbaserad fortsättning ärver aldrig token från den föregående uppgiften.
Använd metoden concurrency::cancellation_token::none när du anropar en konstruktor eller funktion som tar ett
cancellation_tokenobjekt, men du inte vill att åtgärden ska vara cancellable. Om du inte skickar en annulleringstoken till concurrency::task konstruktorn eller funktionen concurrency::create_task, kan den aktiviteten inte avbrytas.
I det här dokumentet
Parallella arbetsträd
PPL använder uppgifter och aktivitetsgrupper för att hantera detaljerade uppgifter och beräkningar. Du kan kapsla aktivitetsgrupper för att bilda träd med parallellt arbete. Följande bild visar ett parallellt arbetsträd. I den här bilden tg1 representerar och tg2 aktivitetsgrupper, t1, t2, t3, t4och t5 det arbete som aktivitetsgrupperna utför.
I följande exempel visas den kod som krävs för att skapa trädet i bilden. I det här exemplet är tg1 och tg2concurrency::structured_task_group-objekt; t1, t2, t3, t4 och t5 är concurrency::task_handle-objekt.
// 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();
}
Du kan också använda klassen concurrency::task_group för att skapa ett liknande arbetsträd.
Concurrency::task class stöder också begreppet ett arbetsträd. Däremot är ett task träd ett beroendeträd. I ett task träd slutförs framtida arbeten efter det pågående arbetet. I ett aktivitetsgruppsträd slutförs det interna arbetet före det yttre arbetet. Mer information om skillnaderna mellan aktiviteter och aktivitetsgrupper finns i Aktivitetsparallellitet.
[Topp]
Avbryta parallella uppgifter
Det finns flera sätt att avbryta parallellt arbete. Det bästa sättet är att använda en annulleringstoken. Aktivitetsgrupper stöder också concurrency::task_group::cancel-metoden och concurrency::structured_task_group::cancel-metoden. Det sista sättet är att utlösa ett undantag i brödtexten för en uppgiftsarbetsfunktion. Oavsett vilken metod du väljer förstår du att annulleringen inte sker omedelbart. Även om nytt arbete inte startas om en aktivitet eller aktivitetsgrupp avbryts, måste aktivt arbete söka efter och svara på annulleringen.
Fler exempel som avbryter parallella uppgifter finns i Genomgång: Ansluta med hjälp av uppgifter och XML HTTP-begäranden, Så här: Använd Annullering för att bryta från en parallell loop och Så här använder du undantagshantering för att bryta från en parallell loop.
Använda en annulleringstoken för att avbryta parallellt arbete
Klasserna task, task_groupoch structured_task_group stöder annullering med hjälp av annulleringstoken. PPL definierar klasserna concurrency::cancellation_token_source och concurrency::cancellation_token för detta ändamål. När du använder en annulleringstoken för att avbryta arbetet startar inte körningen nytt arbete som prenumererar på den token. Arbete som redan är aktivt kan använda medlemsfunktionen is_canceled för att övervaka avbokningstoken och stoppa när den kan.
Starta annulleringen genom att anropa metoden concurrency::cancellation_token_source::cancel . Du svarar på annullering på följande sätt:
För
taskobjekt använder du funktionen concurrency::cancel_current_task .cancel_current_taskavbryter den aktuella aktiviteten och någon av dess värdebaserade fortsättningar. (Den avbryter inte den avbrytartoken som är associerad med uppgiften eller dess fortsättningar.)För aktivitetsgrupper och parallella algoritmer använder du funktionen concurrency::is_current_task_group_canceling för att identifiera avbrytande och återgå så snart som möjligt från taskens innehåll när den här funktionen returnerar
true. (Anropacancel_current_taskinte från en aktivitetsgrupp.)
I följande exempel visas det första grundläggande mönstret för aktivitetsavbokning. Aktivitetstexten söker ibland efter annullering i en loop.
// 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.
*/
Funktionen cancel_current_task kastar. Därför behöver du inte uttryckligen returnera från den nuvarande loopen eller funktionen.
Tips/Råd
Du kan också anropa funktionen concurrency::interruption_point i stället för cancel_current_task.
Det är viktigt att anropa cancel_current_task när du svarar på annullering eftersom uppgiften övergår till det inställda tillståndet. Om du återvänder tidigt i stället för att anropa cancel_current_task, övergår åtgärden till ett slutfört tillstånd, och så körs alla värdebaserade fortsättningar.
Försiktighet
task_canceled Kasta aldrig från din kod. Ring cancel_current_task i stället.
När en uppgift hamnar i tillståndet avbruten, kastar metoden concurrency::task::get ett concurrency::task_canceled-undantag. ( Samtidighet::task::wait returnerar task_status::avbryts och genererar inte.) I följande exempel visas det här beteendet för en aktivitetsbaserad fortsättning. En uppgiftsbaserad fortsättning anropas alltid, även när den föregående uppgiften avbryts.
// 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.
*/
Eftersom värdebaserade kontinuiteter ärver token från sin föregående uppgift om de inte har skapats med en explicit token, träder kontinuiteterna omedelbart in i det avbrutna tillståndet även när den föregående uppgiften fortfarande körs. Därför sprids inte alla undantag som genereras av den föregående aktiviteten efter annulleringen till fortsättningsuppgifterna. Annullering åsidosätter alltid tillståndet för den föregående aktiviteten. Följande exempel liknar det föregående, men illustrerar beteendet för en värdebaserad fortsättning.
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.
*/
Försiktighet
Om du inte skickar en avbrottstoken till task konstruktorn eller funktionen concurrency::create_task, kan den uppgiften inte avbrytas. Dessutom måste du skicka samma annulleringstoken till konstruktorn för alla kapslade uppgifter (det vill säga uppgifter som skapas i brödtexten för en annan uppgift) för att avbryta alla aktiviteter samtidigt.
Du kanske vill köra valfri kod när en annulleringstoken annulleras. Om användaren till exempel väljer knappen Avbryt i användargränssnittet för att avbryta åtgärden kan du inaktivera knappen tills användaren startar en annan åtgärd. I följande exempel visas hur du använder metoden concurrency::cancellation_token::register_callback för att registrera en återanropsfunktion som körs när en annulleringstoken avbryts.
// 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.
*/
Dokumentet Aktivitetsparallellitet förklarar skillnaden mellan värdebaserade och uppgiftsbaserade fortsättningar. Om du inte anger ett cancellation_token objekt för en fortsättningsaktivitet ärver fortsättningen annulleringstoken från den tidigare aktiviteten på följande sätt:
En värdebaserad fortsättning ärver alltid annulleringstoken för den föregående uppgiften.
En aktivitetsbaserad fortsättning ärver aldrig annulleringstoken för den tidigare aktiviteten. Det enda sättet att göra en aktivitetsbaserad fortsättning avbrytbar är att uttryckligen använda en annulleringstoken.
Dessa beteenden påverkas inte av en felaktig uppgift (det vill:en som utlöser ett undantag). I det här fallet avbryts en värdebaserad fortsättning. en aktivitetsbaserad fortsättning avbryts inte.
Försiktighet
En uppgift som skapas i en annan aktivitet (med andra ord en kapslad uppgift) ärver inte annulleringstoken för den överordnade aktiviteten. Endast en värdebaserad fortsättning ärver avbrytningstoken för sin föregående uppgift.
Tips/Råd
Använd metoden concurrency::cancellation_token::none när du anropar en konstruktor eller funktion som tar ett cancellation_token objekt och du inte vill att åtgärden ska vara annullerbar.
Du kan också ange en annulleringstoken till konstruktorn för ett task_group- eller structured_task_group-objekt. En viktig aspekt av detta är att barnaktivitetsgrupper ärver denna cancellation token. Ett exempel som visar det här konceptet med hjälp av funktionen concurrency::run_with_cancellation_token för att köra för att anropa parallel_forfinns i Avbryta parallella algoritmer senare i det här dokumentet.
[Topp]
Avbokningstoken och uppgiftskomposition
Funktionerna concurrency::when_all och concurrency::when_any kan hjälpa dig att skapa flera uppgifter för att implementera vanliga mönster. I det här avsnittet beskrivs hur dessa funktioner fungerar med annulleringstoken.
När du anger en annulleringstoken till antingen when_all funktionen och when_any avbryts funktionen endast när den annulleringstoken avbryts eller när en av deltagaraktiviteterna slutar i ett avbrutet tillstånd eller utlöser ett undantag.
Funktionen when_all ärver annulleringstoken från varje aktivitet som utgör den övergripande åtgärden när du inte anger en annulleringstoken till den. Uppgiften som returneras från when_all avbryts när någon av dessa token avbryts och minst en av deltagaruppgifterna ännu inte har startats eller körs. Ett liknande beteende inträffar när en av aktiviteterna utlöser ett undantag – uppgiften som returneras från when_all avbryts omedelbart med det undantaget.
Körningen väljer annulleringstoken för den aktivitet som returneras från when_any funktionen när aktiviteten är klar. Om ingen av deltagaruppgifterna slutförs i ett fullbordat tillstånd och en eller flera av uppgifterna utlöser ett undantag, väljs en av de uppgifter som utlöste undantaget för att slutföra when_any och dess token väljs som token för den slutliga uppgiften. Om fler än en uppgift har slutförts kommer den uppgift som returneras från when_any uppgiften att sluta i ett slutfört tillstånd. Körmiljön försöker välja en slutförd uppgift vars token inte är avbrutet vid tidpunkten för slutförandet, så att den uppgift som returneras från when_any inte avbryts omedelbart även om andra exekverande uppgifter kan slutföras vid ett senare tillfälle.
[Topp]
Använda avbryt-metoden för att avbryta parallellt arbete
concurrency::task_group::cancel och concurrency::structured_task_group::cancel metoderna ställer in en uppgiftsgrupp i ett avbrutet tillstånd. När du har anropat cancelstartar aktivitetsgruppen inte framtida aktiviteter. Metoderna cancel kan anropas av flera underordnade uppgifter. En avbruten aktivitet får konkurrens::task_group::wait- och konkurrens::structured_task_group::wait-metoderna att returnera konkurrens::canceled.
Om en uppgiftsgrupp avbryts kan anrop från varje underordnad uppgift till exekveringsmiljön utlösa en interruptionspunkt, vilket gör att exekveringsmiljön genererar och fångar en intern undantagstyp för att avbryta aktiva uppgifter. Concurrency Runtime definierar inte specifika avbrottspunkter; de kan inträffa vid vilket anrop till körmiljön som helst. Körtiden måste hantera de undantag som den genererar för att annullera. Hantera därför inte okända undantag i brödtexten för en uppgift.
Om en underordnad uppgift utför en tidskrävande åtgärd och inte anropar till körningen, måste den regelbundet söka efter annullering och avslut i tid. I följande exempel visas ett sätt att avgöra när arbetet avbryts. Aktiviteten t4 avbryter den överordnade aktivitetsgruppen när ett fel uppstår. Ibland anropar aktiviteten t5 metoden structured_task_group::is_canceling för att kontrollera om den avbrutits. Om den överordnade aktivitetsgruppen avbryts skriver aktiviteten t5 ut ett meddelande och avslutas.
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();
Detta exempel kontrollerar för avbokning vid varje 100:e iteration av task-loopen. Frekvensen med vilken du kontrollerar för annullering beror på mängden arbete din uppgift utför och hur snabbt du behöver att uppgifterna svarar på annulleringen.
Om du inte har åtkomst till det överordnade aktivitetsgruppsobjektet anropar du funktionen concurrency::is_current_task_group_canceling för att avgöra om den överordnade aktivitetsgruppen avbryts.
Metoden cancel påverkar endast underordnade uppgifter. Om du till exempel avbryter aktivitetsgruppen tg1 i bilden av det parallella arbetsträdet påverkas alla aktiviteter i trädet (t1, , t2t3, t4och t5). Om du avbryter den kapslade aktivitetsgruppen tg2påverkas endast aktiviteter t4 och t5 .
När du anropar metoden cancel avbryts även alla underordnade uppgiftsgrupper. Annullering påverkar dock inte några föräldrar till aktivitetsgruppen i ett parallellt arbetsträd. Följande exempel visar detta genom att bygga vidare på den parallella arbetsträdsbilden.
Det första av dessa exempel skapar en arbetsfunktion för uppgiften t4, som är underordnad aktivitetsgruppen tg2. Arbetsfunktionen anropar funktionen work i en loop. Om något anrop till work misslyckas så avbryter den uppgiften sin överordnade aktivitetsgrupp. Detta gör att aktivitetsgruppen tg2 går in i det avbrutna läget, men den avbryter inte aktivitetsgruppen tg1.
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;
}
}
});
Det andra exemplet liknar det första, förutom att aktiviteten avbryter aktivitetsgruppen tg1. Detta påverkar alla aktiviteter i trädet (t1, , t2t3, t4och 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;
}
}
});
Klassen structured_task_group är inte trådsäker. Därför genererar en underordnad aktivitet som anropar en metod hos sitt överordnade structured_task_group objekt ett ospecificerat beteende. Undantagen till den här regeln är structured_task_group::cancel och metoderna concurrency::structured_task_group::is_canceling. En underordnad aktivitet kan anropa dessa metoder för att avbryta den överordnade aktivitetsgruppen och söka efter annullering.
Försiktighet
Även om du kan använda en annulleringstoken för att avbryta arbete som utförs av en aktivitetsgrupp som körs som ett underordnat objekt till ett task objekt, kan du inte använda task_group::cancel eller structured_task_group::cancel metoder för att avbryta task objekt som körs i en aktivitetsgrupp.
[Topp]
Använda undantag för att avbryta parallellt arbete
Användningen av annulleringstoken och cancel metoden är effektivare än undantagshantering vid avbokning av ett parallellt arbetsträd. Annulleringstoken och metoden cancel avbryter en uppgift och eventuella underordnade uppgifter uppifrån och ned. Omvänt fungerar undantagshantering nedifrån och upp och måste avbryta varje underordnad aktivitetsgrupp oberoende medan undantaget sprids uppåt. Ämnet Undantagshantering förklarar hur Concurrency Runtime använder undantag för att kommunicera fel. Alla undantag tyder dock inte på ett fel. En sökalgoritm kan till exempel avbryta sin associerade uppgift när den hittar resultatet. Men som tidigare nämnts är undantagshantering mindre effektivt än att använda cancel metoden för att avbryta parallellt arbete.
Försiktighet
Vi rekommenderar att du använder undantag för att avbryta parallellt arbete endast när det behövs. Annulleringstoken och aktivitetsgruppsmetoderna cancel är mer effektiva och mindre felbenägna.
När du utlöser ett undantag i en arbetsfunktions kropp som du passerar till en aktivitetsgrupp, lagrar körsystemet undantaget och överför undantaget till kontexten som väntar på att aktivitetsgruppen blir klar. Precis som med cancel metoden tar körningen bort alla aktiviteter som ännu inte har startats och accepterar inte nya aktiviteter.
Det tredje exemplet liknar det andra, förutom att aktiviteten t4 genererar ett undantag för att avbryta aktivitetsgruppen tg2. I det här exemplet används ett try-catch block för att söka efter annullering när aktivitetsgruppen tg2 väntar på att dess underordnade aktiviteter ska slutföras. Precis som i det första exemplet gör detta att aktivitetsgruppen tg2 går in i det avbrutna tillståndet, men den avbryter inte aktivitetsgruppen tg1.
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;
}
Det fjärde exemplet använder undantagshantering för att avbryta hela arbetsträdet. Exemplet fångar undantaget när aktivitetsgruppen tg1 väntar på att dess underordnade aktiviteter ska slutföras i stället för när aktivitetsgruppen tg2 väntar på sina underordnade aktiviteter. Precis som i det andra exemplet orsakar detta att båda uppgiftsgrupperna i trädet tg1 och tg2 går in i det avbokade tillståndet.
// 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;
}
Eftersom task_group::wait och structured_task_group::wait-metoderna kastar när en underordnad uppgift kastar ett undantag, får du inget returvärde från dessa metoder.
[Topp]
Avbryta parallella algoritmer
Parallella algoritmer i PPL, till exempel parallel_for, bygger på aktivitetsgrupper. Därför kan du använda många av samma tekniker för att avbryta en parallell algoritm.
I följande exempel visas flera sätt att avbryta en parallell algoritm.
I följande exempel används run_with_cancellation_token funktionen för att anropa algoritmen parallel_for . Funktionen run_with_cancellation_token tar en annulleringstoken som ett argument och anropar den angivna arbetsfunktionen synkront. Eftersom parallella algoritmer bygger på uppgifter ärver de annulleringstoken för den överordnade aktiviteten. Därför kan parallel_for svara på annullering.
// 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
*/
I följande exempel används metoden concurrency::structured_task_group::run_and_wait för att anropa algoritmen parallel_for . Metoden structured_task_group::run_and_wait väntar tills den angivna uppgiften har slutförts. Med structured_task_group objektet kan arbetsfunktionen avbryta aktiviteten.
// 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;
}
Det här exemplet genererar följande utdata.
The task group status is: canceled.
I följande exempel används undantagshantering för att avbryta en parallel_for loop. Körningstiden hanterar undantaget till anropskontexten.
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;
}
Det här exemplet genererar följande utdata.
Caught 50
I följande exempel används en boolesk flagga för att samordna annulleringen i en parallel_for loop. Varje aktivitet körs eftersom det här exemplet inte använder cancel metoden eller undantagshanteringen för att avbryta den övergripande uppsättningen aktiviteter. Därför kan den här tekniken ha mer beräkningskostnad jämfört med en avbokningsmekanism.
// 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.
}
});
Varje annulleringsmetod har fördelar jämfört med de andra. Välj den metod som passar dina specifika behov.
[Topp]
När du inte ska använda annullering
Användningen av annullering är lämplig när varje del i en grupp med relaterade uppgifter kan avslutas i rätt tid. Det finns dock vissa scenarier där annullering kanske inte är lämpligt för ditt program. Eftersom aktivitetsavbokningen till exempel är samarbetsinriktad avbryts inte den övergripande uppsättningen aktiviteter om någon enskild aktivitet blockeras. Om en aktivitet till exempel inte har startats ännu, men en annan aktiv aktivitet avblockeras, startar den inte om aktivitetsgruppen avbryts. Detta kan orsaka att ett dödläge uppstår i ditt program. Ett andra exempel på när det kanske inte är lämpligt att avbryta en aktivitet är när en aktivitet avbryts, men dess underordnade uppgift utför en viktig åtgärd, till exempel att frigöra en resurs. Eftersom den övergripande uppsättningen aktiviteter avbryts när den överordnade aktiviteten avbryts körs inte åtgärden. Ett exempel som illustrerar den här punkten finns i avsnittet Förstå hur annullering och undantagshantering påverkar objektförstörelse i avsnittet Metodtips i biblioteket parallella mönster.
[Topp]
Relaterade ämnen
| Titel | Beskrivning |
|---|---|
| Anvisningar: Använda Annullering för att bryta från en parallell loop | Visar hur du använder annullering för att implementera en parallell sökalgoritm. |
| Gör så här: Använd undantagshantering för att bryta från en parallell loop | Visar hur du använder task_group klassen för att skriva en sökalgoritm för en grundläggande trädstruktur. |
| undantagshantering | Beskriver hur körningen hanterar undantag som genereras av aktivitetsgrupper, lätta uppgifter och asynkrona agenter och hur du svarar på undantag i dina program. |
| Uppgiftsparallellitet | Beskriver hur aktiviteter relaterar till aktivitetsgrupper och hur du kan använda ostrukturerade och strukturerade uppgifter i dina program. |
| Parallella algoritmer | Beskriver parallella algoritmer, som samtidigt utför arbete med samlingar av data |
| PPL (Parallel Patterns Library) | Ger en översikt över Parallel Patterns-biblioteket. |