Sdílet prostřednictvím


Úkol rovnoběžnosti (souběžnosti Runtime)

Tento dokument popisuje roli skupiny úloh v souběžném běhu a úkolů.A úkolu je jednotka práce, který provádí specifické úlohy.Obvykle běží souběžně s jinými úkoly a úlohy lze rozložit na více uzamykání, další úkoly.A skupiny úloh uspořádává kolekci úkolů.

Úkoly používáte při zápisu asynchronního kódu a chcete některé operace nastat po dokončení asynchronní operace.Můžete například použít úkol asynchronně číst ze souboru a pokračování úkol, který je vysvětlen dále v tomto dokumentu, zpracování dat, až bude k dispozici.Naopak použijte rozložit na menší kousky paralelní pracovní skupiny úkolů.Předpokládejme například, že máte rekurzivní algoritmus, který rozdělí na dva oddíly zbývající práce.Skupiny úloh můžete současně spustit tyto oddíly a potom čekat rozdělené práci dokončit.

Tip

Pokud chcete použít stejnou rutinní každý prvek kolekce paralelně, použijte jako paralelní algoritmus concurrency::parallel_for, úkolu nebo skupiny úloh.Další informace o paralelní algoritmy Paralelní algoritmy.

Klíč bodů

  • Při předání proměnných lambda výraz odkazem musí zaručit, že existence této proměnné potrvá až do dokončení úkolu.

  • Použití úkoly ( concurrency::task třídy) při psaní kódu pro asynchronní.

  • Použití skupin úloh (jako concurrency::task_group třídy nebo concurrency::parallel_invoke algoritmus) potřebujete rozložit na menší kousky paralelní pracovní a počkejte, než ty menší kusy dokončit.

  • Použití concurrency::task::then k vytvoření continuations.A pokračování je úkol, který bude spuštěn asynchronně po dokončení jiného úkolu.Můžete připojit libovolný počet continuations tvoří řetězec asynchronní práci.

  • Po dokončení úkolu antecedent i když je zrušena nebo vyvolá výjimku antecedent úkolu je vždy pokračování podle úloh naplánovat provedení.

  • Použití concurrency::when_all vytvořit úkol, který dokončí po dokončení každého člena sady úkolů.Použití concurrency::when_any vytvořit úkol, který dokončí po dokončení jednoho člena sady úkolů.

  • Úkoly a skupiny úloh účastnit mechanismus zrušení PPL.Další informace naleznete v tématu Zrušení v PPL.

  • Informace o zpracování výjimek vyvolaných úkoly a skupiny úloh modulu runtime, viz Zpracování výjimek v souběžném běhu.

V tomto dokumentu

  • Použití výrazů Lambda

  • Třídy úloh

  • Pokračující úlohy

  • Hodnota založena Versus Continuations podle úloh.

  • Vytváření úkolů

    • Funkce when_all

    • Funkce when_any

  • Zpožděné spuštění úlohy.

  • Skupiny úkolů

  • Porovnání task_group na structured_task_group

  • Příklad

  • Robustní programování

Použití výrazů Lambda

Lambda výrazy jsou běžným způsobem definovat práce, která provádí úkoly a skupin úkolů z důvodu jejich stručné syntaxi.Zde jsou některé tipy pro jejich použití:

  • Protože úkoly obvykle spustit na pozadí podprocesů, mějte při zachycení proměnné ve výrazech lambda životnost objektu.Při zachycení Proměnná hodnota kopie této proměnné se v těle lambda.Při zachycení odkazem není vytvořena její kopie.Proto zkontrolujte, zda, outlives platnosti libovolné proměnné zachytíte odkazem na úkol, který ji používá.

  • Obecně není zachytit proměnných, jsou přiděleny v zásobníku.To také znamená neměl zachytávání členské proměnné objektů, které jsou přiděleny v zásobníku.

  • Být explicitní proměnných sbíraných lambda výrazy a pomáhá určit, co je při digitalizaci hodnotu oproti odkazem.Z tohoto důvodu nedoporučujeme používat [=] nebo [&] možnosti lambda výrazů.

Jeden společný vzorek je jeden úkol v řetězci pokračování přiřadí proměnné a jiný úkol čte této proměnné.Hodnotu nelze zachytit, protože každý úkol pokračování uchovává kopii této proměnné různých.Pro přidělené zásobníku proměnné také nelze zachytit odkazem protože proměnná pravděpodobně již platná.

Chcete-li tento problém vyřešit, použijte inteligentní ukazatel jako std::shared_ptrobtékání proměnné a předat inteligentní ukazatel hodnotu.Tímto způsobem základní objekt lze přiřadit a čtení z a bude outlive úkoly, které ji používají.Tento postup použijte i v případě, že je ukazatel nebo úchyt se počítají referenční proměnná (^) do objektu modulu Runtime v systému Windows.Zde je základní příklad:

// lambda-task-lifetime.cpp
// compile with: /EHsc
#include <ppltasks.h>
#include <iostream>
#include <string>

using namespace concurrency;
using namespace std;

task<wstring> write_to_string()
{
    // Create a shared pointer to a string that is 
    // assigned to and read by multiple tasks.
    // By using a shared pointer, the string outlives
    // the tasks, which can run in the background after
    // this function exits.
    auto s = make_shared<wstring>(L"Value 1");

    return create_task([s] 
    {
        // Print the current value.
        wcout << L"Current value: " << *s << endl;
        // Assign to a new value.
        *s = L"Value 2";

    }).then([s] 
    {
        // Print the current value.
        wcout << L"Current value: " << *s << endl;
        // Assign to a new value and return the string.
        *s = L"Value 3";
        return *s;
    });
}

int wmain()
{
    // Create a chain of tasks that work with a string.
    auto t = write_to_string();

    // Wait for the tasks to finish and print the result.
    wcout << L"Final value: " << t.get() << endl;
}

/* Output:
    Current value: Value 1
    Current value: Value 2
    Final value: Value 3
*/

Více informací o lambda výrazů naleznete v Lambda výrazy v jazyce C++.

Top

Třídy úloh

Můžete použít concurrency::task třídy k vytvoření úlohy do sady závislé operace.Tento model složení je podporován pojem continuations.Kód umožňuje pokračování mají být provedeny při předchozí, nebo antecedent, dokončení úkolu.Výsledek antecedent úlohy je předáno jako vstup pro jeden nebo více úkolů pokračování.Pokračování úkoly, které čekají na něj jsou po dokončení antecedent úkol naplánovat provedení.Každý úkol pokračování obdrží kopii výsledek antecedent úkolu.Pokračování úkoly, může být antecedent úkoly pro ostatní continuations tím vytvořením řetězu úkoly.Continuations vám pomůže vytvořit libovolné délky řetězce úkoly, které mají zvláštní závislosti mezi nimi.Kromě toho můžete úkol zúčastnit, zrušení spustí buď před úkoly nebo způsob spolupráce je spuštěn.Další informace o tomto modelu zrušení viz Zrušení v PPL.

taskje-li šablonu třídy.Parametr typu T je typ výsledku vytvořené úlohy.Tento typ může být void Pokud úkol nevrací hodnotu.Tnelze použít const modifikátor.

Po vytvoření úkolu poskytnout pracovní funkce těla úkolu, který plní.Tato funkce práce dodává ve formě lambda funkce, ukazatel na funkci nebo funkce objektu.Čekání na úkol dokončit bez získání výsledku, zavolejte concurrency::task::wait metoda.task::wait Metoda vrátí concurrency::task_status hodnotu, která popisuje, zda byla úloha dokončena nebo zrušena.Získání výsledku úkol volání concurrency::task::get metoda.Tato metoda volá task::wait je k dispozici výsledek počkat úkol dokončit, a proto blokuje spuštění aktuálního podprocesu.

Následující příklad ukazuje, jak vytvořit úkol, čekat na jeho výsledek a zobrazit jeho hodnotu.Příklady v této dokumentaci pomocí funkcí lambda, protože poskytují více stručné syntaxe.Však můžete také ukazatele funkce a objekty funkce při používání úkolů.

// basic-task.cpp
// compile with: /EHsc
#include <ppltasks.h>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain()
{
    // Create a task.
    task<int> t([]()
    {
        return 42;
    });

    // In this example, you don't necessarily need to call wait() because
    // the call to get() also waits for the result.
    t.wait();

    // Print the result.
    wcout << t.get() << endl;
}

/* Output:
    42
*/

Concurrency::create_task funkce umožňuje použít auto klíčové místo deklarace typu.Zvažte například následující kód, který vytvoří a vytiskne jednotková matice:

// create-task.cpp
// compile with: /EHsc
#include <ppltasks.h>
#include <string>
#include <iostream>
#include <array>

using namespace concurrency;
using namespace std;

int wmain()
{
    task<array<array<int, 10>, 10>> create_identity_matrix([]
    {
        array<array<int, 10>, 10> matrix;
        int row = 0;
        for_each(begin(matrix), end(matrix), [&row](array<int, 10>& matrixRow) 
        {
            fill(begin(matrixRow), end(matrixRow), 0);
            matrixRow[row] = 1;
            row++;
        });
        return matrix;
    });

    auto print_matrix = create_identity_matrix.then([](array<array<int, 10>, 10> matrix)
    {
        for_each(begin(matrix), end(matrix), [](array<int, 10>& matrixRow) 
        {
            wstring comma;
            for_each(begin(matrixRow), end(matrixRow), [&comma](int n) 
            {
                wcout << comma << n;
                comma = L", ";
            });
            wcout << endl;
        });
    });

    print_matrix.wait();
}
/* Output:
    1, 0, 0, 0, 0, 0, 0, 0, 0, 0
    0, 1, 0, 0, 0, 0, 0, 0, 0, 0
    0, 0, 1, 0, 0, 0, 0, 0, 0, 0
    0, 0, 0, 1, 0, 0, 0, 0, 0, 0
    0, 0, 0, 0, 1, 0, 0, 0, 0, 0
    0, 0, 0, 0, 0, 1, 0, 0, 0, 0
    0, 0, 0, 0, 0, 0, 1, 0, 0, 0
    0, 0, 0, 0, 0, 0, 0, 1, 0, 0
    0, 0, 0, 0, 0, 0, 0, 0, 1, 0
    0, 0, 0, 0, 0, 0, 0, 0, 0, 1
*/

Můžete použít create_task funkci vytvořit odpovídající operace.

auto create_identity_matrix = create_task([]
{
    array<array<int, 10>, 10> matrix;
    int row = 0;
    for_each(begin(matrix), end(matrix), [&row](array<int, 10>& matrixRow) 
    {
        fill(begin(matrixRow), end(matrixRow), 0);
        matrixRow[row] = 1;
        row++;
    });
    return matrix;
});

Pokud se během provádění úkolu je vyvolána výjimka, modul runtime marshals výjimku v následné volání task::get nebo task::wait, nebo pokračování podle úloh.Další informace o úkolu mechanismus zpracování výjimek v Zpracování výjimek v souběžném běhu.

Příklad, který používá task, concurrency::task_completion_event, zrušení, viz Názorný postup: Připojení pomocí úlohy a požadavek HTTP XML (IXHR2).( task_completion_event Třídy je popsán dále v tomto dokumentu.)

Tip

Informace o podrobnosti, které jsou specifické pro úkoly v Windows Store apps, viz Asynchronous programming in C++ a Vytváření asynchronních operací v jazyce C++ pro aplikace pro web Windows Store.

Top

Pokračující úlohy

V asynchronním programování je velmi běžné, že jedna asynchronní operace při dokončení vyvolá druhou operaci a předá ji data.Tradičně to se provádí pomocí metody zpětného volání.V modulu Runtime souběžnosti stejné funkce poskytované pokračování úlohy.Pokračování úlohy (známé také jako pokračování) je asynchronní úkol, který je vyvolán jiný úkol, který je označován jako antecedent, po dokončení antecedent.Pomocí continuations můžete:

  • Předejte data z antecedent pokračování.

  • Určete přesné podmínky, za nichž je pokračování vyvolat nebo není vyvolána.

  • Zrušte pokračování buď před spuštěním nebo zavedení je spuštěn.

  • Poskytuje tipy týkající se jak naplánovat pokračování.(Platí pro Windows Store pouze aplikacím.Další informace naleznete v tématu Vytváření asynchronních operací v jazyce C++ pro aplikace pro web Windows Store.)

  • Vyvoláte více continuations ze stejné antecedent.

  • Po dokončení všech nebo některých více anamnézy, vyvoláte jedno pokračování.

  • Řetěz continuations jeden po druhém k libovolné délky.

  • Pokračování slouží ke zpracování výjimek, které jsou vyvolané antecedent.

Tyto funkce umožňují provést jednu nebo více úloh po dokončení prvního úkolu.Můžete například vytvořit pokračování, který komprimuje soubor po první úkol čte z disku.

Následující příklad upravuje předchozímu použití concurrency::task::then metoda plánování pokračování, který vytiskne hodnotu antecedent úkolu, když je k dispozici.

// basic-continuation.cpp
// compile with: /EHsc
#include <ppltasks.h>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain()
{
    auto t = create_task([]() -> int
    {
        return 42;
    });

    t.then([](int result)
    {
        wcout << result << endl;
    }).wait();

    // Alternatively, you can chain the tasks directly and
    // eliminate the local variable.
    /*create_task([]() -> int
    {
        return 42;
    }).then([](int result)
    {
        wcout << result << endl;
    }).wait();*/
}

/* Output:
    42
*/

Můžete zřetězit a vnořit úkoly libovolné délky.Úkol může mít také více continuations.Následující příklad ukazuje základní pokračování řetězu, která zvýší hodnotu předchozí úkol třikrát.

// continuation-chain.cpp
// compile with: /EHsc
#include <ppltasks.h>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain()
{
    auto t = create_task([]() -> int
    { 
        return 0;
    });

    // Create a lambda that increments its input value.
    auto increment = [](int n) { return n + 1; };

    // Run a chain of continuations and print the result.
    int result = t.then(increment).then(increment).then(increment).get();
    wcout << result << endl;
}

/* Output:
    3
*/

Pokračování může také vracet jiného úkolu.Pokud neexistuje žádné zrušení, tento úkol proveden před další pokračování.Tato technika je znám jako asynchronní rozvinutím.Asynchronní rozvinutím je užitečné, pokud chcete provést dodatečné práce na pozadí, ale nechcete blokovat aktuální podproces aktuální úlohy.(To je běžné v Windows Store apps, kde lze spustit continuations uživatelské rozhraní).Následující příklad ukazuje tři úkoly.Vrátí první úkol jiného úkolu se spouští před pokračování úlohy.

// async-unwrapping.cpp
// compile with: /EHsc
#include <ppltasks.h>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain()
{
    auto t = create_task([]()
    {
        wcout << L"Task A" << endl;

        // Create an inner task that runs before any continuation
        // of the outer task.
        return create_task([]()
        {
            wcout << L"Task B" << endl;
        });
    });

    // Run and wait for a continuation of the outer task.
    t.then([]()
    {
        wcout << L"Task C" << endl;
    }).wait();
}

/* Output:
    Task A
    Task B
    Task C
*/
Důležitá poznámkaDůležité

Při pokračování úkolu vrátí vnořené úkolu typu N, výsledný úkol má typ N, není task<N>a po dokončení úkolu vnořené dokončí.Jinými slovy pokračování provádí rozvinutím vnořené úkolu.

Top

Hodnota založena Versus Continuations podle úloh.

Vzhledem task objektu, jehož typ vrácené T, zadáte hodnotu typu T nebo task<T> jeho pokračování úkolům.Pokračování, který typ T se označuje jako hodnota založena pokračování.Hodnota založena pokračování je naplánováno spuštění antecedent úloha dokončena bez chyb a zrušena.Pokračování, který typ task<T> jako svůj parametr je označován jako podle úloh pokračování.Po dokončení úkolu antecedent i když je zrušena nebo vyvolá výjimku antecedent úkolu je vždy pokračování podle úloh naplánovat provedení.Potom můžete volat task::get k získání požadovaného výsledku antecedent úkolu.Pokud antecedent úkol byl zrušen, task::get vyvolá concurrency::task_canceled.Pokud úkol antecedent došlo k výjimce task::get znovu výjimku.Pokračování podle úloh není označena jako zrušená při jeho úkolu antecedent je zrušena.

Top

Vytváření úkolů

Tato část popisuje concurrency::when_all a concurrency::when_any funkce, které vám pomohou vytvořit více úkolů pro provádění společných vzorů.

Dd492427.collapse_all(cs-cz,VS.110).gifFunkce when_all

when_all Funkce vytvoří úkol dokončí po dokončení sady úkolů.Tato funkce vrací std::vector objekt, který obsahuje výsledek každého úkolu v sadě.V následujícím příkladu základní when_all k vytvoření úkolu, který představuje dokončení tři úkoly.

// join-tasks.cpp
// compile with: /EHsc
#include <ppltasks.h>
#include <array>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain()
{
    // Start multiple tasks.
    array<task<void>, 3> tasks = 
    {
        create_task([] { wcout << L"Hello from taskA." << endl; }),
        create_task([] { wcout << L"Hello from taskB." << endl; }),
        create_task([] { wcout << L"Hello from taskC." << endl; })
    };

    auto joinTask = when_all(begin(tasks), end(tasks));

    // Print a message from the joining thread.
    wcout << L"Hello from the joining thread." << endl;

    // Wait for the tasks to finish.
    joinTask.wait();
}

/* Sample output:
    Hello from the joining thread.
    Hello from taskA.
    Hello from taskC.
    Hello from taskB.
*/

[!POZNÁMKA]

Úkoly, které předáte do when_all musí být jednotné.Jinými slovy musí všechny vrátit stejného typu.

Můžete také použít && syntaxe vyrábět úkol dokončí po dokončení sady úloh, jak ukazuje následující příklad.

auto t = t1 && t2; // same as when_all

Je běžné použití pokračování společně s when_all k provedení akce po dokončení sady úkolů.Následující příklad upravuje předchozímu tisknout součet tři úkoly vytvoří každý int výsledek.

// Start multiple tasks.
array<task<int>, 3> tasks =
{
    create_task([]() -> int { return 88; }),
    create_task([]() -> int { return 42; }),
    create_task([]() -> int { return 99; })
};

auto joinTask = when_all(begin(tasks), end(tasks)).then([](vector<int> results)
{
    wcout << L"The sum is " 
          << accumulate(begin(results), end(results), 0)
          << L'.' << endl;
});

// Print a message from the joining thread.
wcout << L"Hello from the joining thread." << endl;

// Wait for the tasks to finish.
joinTask.wait();

/* Output:
    Hello from the joining thread.
    The sum is 229.
*/

V tomto příkladu, můžete také zadat task<vector<int>> k výrobě pokračování podle úloh.

Poznámka k upozorněníUpozornění

Pokud je libovolný úkol v sadu úkolů je zrušena nebo vyvolá výjimku, when_all ihned dokončí a nečeká na dokončení zbývající úkoly.Pokud se vyskytne výjimka, modul runtime znovu výjimku při volání task::get nebo task::wait na task objektu, který when_all vrátí.Pokud více než jeden úkol vyvolá, runtime zvolí jeden z nich.Pokud jeden vyvolá výjimku, zajistit proto čekat na dokončení všech úloh.

Top

Dd492427.collapse_all(cs-cz,VS.110).gifFunkce when_any

when_any Funkce vytvoří úkol dokončí po dokončení prvního úkolu v sadu úkolů.Tato funkce vrací std::pair objekt obsahující výsledek o dokončení úkolu a index úkolu v sadě.

when_any Funkce je užitečná v následujících situacích:

  • Nadbytečná operace.Zvažte algoritmus nebo operaci lze provést několika způsoby.Můžete použít when_any funkce dokončení první operace a potom zrušení zbývajících operací.

  • Prokládaném operace.Můžete spustit více operací, všechny musí dokončit a použít when_any funkce pro zpracování výsledků při ukončení každé operace.Po dokončení jedné operace můžete spustit jednu nebo více dalších úkolů.

  • Omezené operace.Můžete použít when_any funkce rozšíření předchozí situaci omezením počtu souběžných operací.

  • Operace vypršela.Můžete použít when_any funkce vybrat mezi jednu nebo více úloh a úlohy, která po určitém čase.

Stejně jako when_all, je běžné použití pokračování s when_any k provedení akce po první sady úkolů.V následujícím příkladu základní when_any vytvořit úlohu, která dokončí po dokončení první tři úkoly.

// select-task.cpp
// compile with: /EHsc
#include <ppltasks.h>
#include <array>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain()
{
    // Start multiple tasks.
    array<task<int>, 3> tasks = {
        create_task([]() -> int { return 88; }),
        create_task([]() -> int { return 42; }),
        create_task([]() -> int { return 99; })
    };

    // Select the first to finish.
    when_any(begin(tasks), end(tasks)).then([](pair<int, size_t> result)
    {
        wcout << "First task to finish returns "
              << result.first
              << L" and has index "
              << result.second
              << L'.' << endl;
    }).wait();
}

/* Sample output:
    First task to finish returns 42 and has index 1.
*/

V tomto příkladu, můžete také zadat task<pair<int, size_t>> k výrobě pokračování podle úloh.

[!POZNÁMKA]

Stejně jako when_all, úkoly, které předáte do when_any všechny vracet stejný typ.

Můžete také použít || syntaxe vyrábět úkol dokončí po dokončení prvního úkolu v sadu úkolů, jak ukazuje následující příklad.

auto t = t1 || t2; // same as when_any

Top

Zpožděné spuštění úlohy.

Někdy je nutné zpoždění spuštění úlohy, dokud je splněna podmínka nebo spuštění úlohy v reakci na vnější události.V asynchronním programování pravděpodobně například spuštění úlohy v odezvě na událost ukončení I/O.

Jsou dva způsoby, jak toho dosáhnout použití pokračování nebo ke spuštění úlohy a čekání na událost uvnitř funkce práce úkolu.Existují však případy, kdy není jej možné použít jeden z těchto postupů.Například pro vytvoření pokračování, musí mít antecedent úkolu.Však pokud nemáte antecedent úkolu, můžete vytvořit událostí dokončení úkolu a bude k dispozici později zřetězení událost dokončení úkolu antecedent.Navíc protože čekání úkol také blokuje podproces, můžete pomocí události dokončení úloh při dokončení asynchronní operace provádět práce a proto volný podproces.

Concurrency::task_completion_event třídy pomáhá zjednodušit složení těchto úkolů.Podobně jako task třídy parametr typu T je typ výsledku vytvořené úlohy.Tento typ může být void Pokud úkol nevrací hodnotu.Tnelze použít const modifikátor.Obvykle task_completion_event objekt je poskytován na vlákno nebo úkol, který bude signál ji, jakmile je hodnota k dispozici.Současně jsou jako posluchače událost nastavit jeden nebo více úkolů.Při nastavení události dokončit úkoly posluchače a jejich continuations je naplánováno spuštění.

Příklad, který používá task_completion_event k provedení úkolu, který dokončí po prodlevě, viz Jak: vytvoření úkolu, který dokončí po prodlevě.

Top

Skupiny úkolů

A skupiny úloh uspořádává kolekci úkolů.Skupiny úloh push úlohy do fronty práce krádeži.Plánovač úkolů odebere z této fronty a provede jejich výpočetní prostředky.Po přidání úkolů do skupiny úkolů může čekat všechny úkoly dokončit nebo Storno dosud nezahájené úkoly.

Používá PPL concurrency::task_group a concurrency::structured_task_group třídy představují skupiny úkolů a concurrency::task_handle třídy úloh spouštěných v těchto skupinách.task_handle Třídy zapouzdří kód, který vykonává práci.Podobně jako task třída pracovní funkce pochází ve formě lambda funkce, ukazatel na funkci nebo funkce objektu.Obvykle není nutné pracovat s task_handle objekty přímo.Místo toho předat funkce pracovní skupiny úloh a úloh skupiny vytvoří a spravuje task_handle objektů.

PPL rozděluje skupiny úkolů na tyto dvě kategorie: skupin úkolů nestrukturovaných a skupiny Pokročilí.PPL používá task_group třídy skupiny nestrukturovaný úloh a structured_task_group třídy Pokročilí skupin.

Důležitá poznámkaDůležité

Definuje také PPL concurrency::parallel_invoke algoritmus, který používá structured_task_group třídy spustit sadu úkolů současně.Protože parallel_invoke algoritmus má více stručné syntaxi, doporučujeme použít místo něj structured_task_group třídy, pokud to bude možné.Téma Paralelní algoritmy popisuje parallel_invoke podrobněji.

Použití parallel_invoke Pokud máte několik nezávislých úloh, které chcete spustit současně a všechny úkoly dokončit před pokračováním musí čekat.Tato technika je často označována jako větve a spojení rovnoběžnosti.Použití task_group Pokud máte několik nezávislých úloh, které chcete spouštět současně, ale chcete čekat na dokončení později úkolů.Můžete například přidat úkoly task_group objekt a počkejte na dokončení jiné funkce nebo z jiného podprocesu úkolů.

Skupiny úloh podporují koncept zrušení.Zrušení umožňuje signál všechny aktivní úkoly chcete zrušit celkové operace.Zrušení také zabraňuje dosud nezahájené spuštění úkoly.Další informace o zrušení viz Zrušení v PPL.

Modul runtime poskytuje také zpracování výjimek modelu, který umožňuje vyvoláním výjimky z úkolu a zpracovat výjimku při čekání skupiny úkol dokončit.Další informace o tomto modelu zpracování výjimek, viz Zpracování výjimek v souběžném běhu.

Top

Porovnání task_group na structured_task_group

Přestože doporučujeme použít task_group nebo parallel_invoke místo structured_task_group třídy, jsou případy, kdy chcete použít structured_task_group, například při psaní paralelní algoritmus, který provádí proměnný počet úkolů nebo vyžaduje podporu pro zrušení.Tato část vysvětluje rozdíly mezi task_group a structured_task_group třídy.

task_group Je třída podprocesu.Proto můžete přidat úkoly task_group z více podprocesů objekt a počkejte nebo zrušit task_group objektu více podprocesů.Konstrukce a zničení structured_task_group objektu musí dojít v rámci stejného oboru lexikální.Kromě toho všechny operace v structured_task_group objektu musí dojít ve stejném podprocesu.Výjimkou z tohoto pravidla je concurrency::structured_task_group::cancel a concurrency::structured_task_group::is_canceling metod.Tyto metody zrušit nadřazené skupině úloh nebo zrušení kdykoli zkontrolovat lze volat podřízeného úkolu.

Další úkoly lze spustit task_group objektu po volání concurrency::task_group::wait nebo concurrency::task_group::run_and_wait metoda.Naopak, nelze spustit další úkoly v structured_task_group objektu po volání concurrency::structured_task_group::wait nebo concurrency::structured_task_group::run_and_wait metod.

Protože structured_task_group třídy mezi podprocesy není synchronizováno, má méně spuštění nároky než task_group třídy.Proto pokud problém, který nevyžaduje plánování práce z více podprocesů a nelze použít parallel_invoke algoritmus structured_task_group třídy můžete zapsat kód lépe provést.

Pokud používáte jednu structured_task_group objektu uvnitř jiného structured_task_group objektu, vnitřní objekt musí být dokončen a zničení před dokončením vnější objekt.task_group Třídy nevyžaduje pro skupiny vnořené úkol dokončit před dokončením vnější skupiny.

Nestrukturovaný úkol skupiny a skupiny Pokročilí pracovat s úchyty úkol různými způsoby.Pracovní funkce můžete předat přímo task_group objektu; task_group vytvoří a správě úloh popisovač objektu.structured_task_group Vyžaduje třídu můžete spravovat task_handle objekt pro každý úkol.Každý task_handle zůstává platné po celou dobu trvání jeho přidružený objekt structured_task_group objektu.Použití concurrency::make_task funkci vytvořit task_handle objektu, jak je znázorněno v následujícím příkladu:

// make-task-structure.cpp
// compile with: /EHsc
#include <ppl.h>

using namespace concurrency;

int wmain()
{
   // Use the make_task function to define several tasks.
   auto task1 = make_task([] { /*TODO: Define the task body.*/ });
   auto task2 = make_task([] { /*TODO: Define the task body.*/ });
   auto task3 = make_task([] { /*TODO: Define the task body.*/ });

   // Create a structured task group and run the tasks concurrently.

   structured_task_group tasks;

   tasks.run(task1);
   tasks.run(task2);
   tasks.run_and_wait(task3);
}

Správa úloh úchyty pro případy, kdy mají být proměnné počet úkolů, použijte přidělení zásobníku rutina jako _malloca kontejner třídy, nebo jako std::vector.

I task_group a structured_task_group zrušení podporují.Další informace o zrušení viz Zrušení v PPL.

Top

Příklad

Následující základní příklad ukazuje, jak pracovat s skupiny úloh.V tomto příkladu parallel_invoke algoritmus dvou plnit úkoly současně.Každý úkol přidá dílčích úkolů, které task_group objektu.Všimněte si, že task_group třídy umožňuje přidat úkoly do něj souběžně více úkolů.

// using-task-groups.cpp
// compile with: /EHsc
#include <ppl.h>
#include <sstream>
#include <iostream>

using namespace concurrency;
using namespace std;

// Prints a message to the console.
template<typename T>
void print_message(T t)
{
   wstringstream ss;
   ss << L"Message from task: " << t << endl;
   wcout << ss.str(); 
}

int wmain()
{  
   // A task_group object that can be used from multiple threads.
   task_group tasks;

   // Concurrently add several tasks to the task_group object.
   parallel_invoke(
      [&] {
         // Add a few tasks to the task_group object.
         tasks.run([] { print_message(L"Hello"); });
         tasks.run([] { print_message(42); });
      },
      [&] {
         // Add one additional task to the task_group object.
         tasks.run([] { print_message(3.14); });
      }
   );

   // Wait for all tasks to finish.
   tasks.wait();
}

Ukázkový výstup v tomto příkladu je následující:

Message from task: Hello
Message from task: 3.14
Message from task: 42

Protože parallel_invoke algoritmus souběžně spustí úlohy, pořadí výstupní zprávy by se mohly lišit.

Kompletní příklady použití parallel_invoke algoritmus, viz Postup: zápis paralelní rutinní řazení pomocí parallel_invoke a Jak: použití parallel_invoke k provedení paralelní operace.Kompletní příklad, který používá task_group třída implementovat asynchronní termínů naleznete v tématu Názorný postup: Termíny provádění.

Top

Robustní programování

Ujistěte se, že porozumět roli zrušení a při použití úkoly skupiny úkolů a paralelní algoritmy zpracování výjimek.Například ve stromu paralelní pracovní úkol, který je zrušena zabraňuje podřízené úkoly spuštění.To může způsobit problémy, pokud jeden z podřízených úkolů provádí operaci, která je důležité aplikace, například uvolnění prostředku.Navíc pokud podřízeného úkolu vyvolá výjimku, výjimku by mohly šířit prostřednictvím destruktoru objektu a způsobit nedefinované chování aplikace.Příklad ilustruje tyto body, najdete Understand how Cancellation and Exception Handling Affect Object Destruction části Doporučené postupy v paralelních vzorků knihovny dokumentu.Další informace o zrušení a modely zpracování výjimek v PPL Zrušení v PPL a Zpracování výjimek v souběžném běhu.

Top

Příbuzná témata

Title

Description

Postup: zápis paralelní rutinní řazení pomocí parallel_invoke

Použití parallel_invoke algoritmus pro zlepšení výkonu algoritmus řazení bitonic.

Jak: použití parallel_invoke k provedení paralelní operace

Použití parallel_invoke algoritmus ke zlepšení výkonu programu, který provádí operace více sdílené datové zdroje.

Jak: vytvoření úkolu, který dokončí po prodlevě

Použití task, cancellation_token_source, cancellation_token, a task_completion_event třídy vytvořit úkol, který dokončí po prodlevě.

Názorný postup: Termíny provádění

Ukazuje, jak sloučit existující funkce v souběžném běhu do něco více nemá.

Paralelní knihovnu vzorků (PPL)

Popisuje PPL, poskytující naprosto programovací model pro vývoj aplikací souběžně.

Odkaz

úkol třídy (souběžnosti Runtime)

Třída task_completion_event

when_all funkce

when_any funkce

Třída task_group

parallel_invoke funkce

Třída structured_task_group