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

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, t2t3, , t4a t5 představují práci, kterou skupiny úkolů provádějí.

Paralelní pracovní strom.

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, t4a 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 taska , task_grouppodporují 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. (Nevolejte cancel_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_taskconcurrency::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_taskvrá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.

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í cancelskupina ú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, t4a t5). Pokud zrušíte vnořenou skupinu úloh, tg2budou 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, t4a 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_forvychá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]

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)

cancellation_token_source – třída

cancellation_token – třída

task_group – třída

structured_task_group – třída

parallel_for – funkce