任务并行(并发运行时)

本文档介绍并发运行时中任务和任务组的角色。 “任务”是执行特定作业的工作单元。 任务通常运行与其他任务并行,并且可以分解成此外,更为详细,任务。 “任务组”可组织任务的集合。

在异步操作完成后,请使用任务,当您编写异步代码并希望某项操作发生。 例如,可以使用任务异步读取文件和延续任务,则稍后部分进行介绍,处理数据,则将变为可用后。 相反,使用求解并行工作的任务组分为多个较小的部分。 例如,假设您使用递归算法将剩余工时分成两部分。 您可以使用任务组同时运行这些分区,然后等待该部件的工作完成。

提示

当要并行时将同一实例应用于集合的每个元素,请使用并行算法,例如 concurrency::parallel_for,而不是任务或任务组。有关并行算法的更多信息,请参见并行算法

关键点

  • 如果通过引用将变量传递到 lambda 表达式,您必须保证此变量的生存期能够一直持续到任务完成。

  • 使用 任务 ( concurrency::task 选件类),当您编写异步代码。

  • 使用任务组(例如 concurrency::task_group 选件类或 concurrency::parallel_invoke 算法),您需要并行工作分解为更小的部分然后等待这些小部分完成。

  • 使用 concurrency::task::then 方法创建延续。 继续 是以异步方式运行的任务,其他任务完成后。 可以连接任意数量的连续窗体异步工作链。

  • 基于任务的继续为执行始终计划,在前面的任务完成时,因此,即使在前面的任务已取消或引发异常。

  • 使用 concurrency::when_all 创建完成的任务,在每个成员的一组任务完成后。 使用 concurrency::when_any 创建完成的任务,在某个成员的一组任务完成后。

  • 任务和任务组可以参与 PPL 取消机制。 有关更多信息,请参见PPL 中的取消操作

  • 了解由任务和任务组引发的运行时处理异常,请参见 并发运行时中的异常处理

在本文档

  • 使用 Lambda 表达式

  • 任务选件类

  • 延续任务

  • 基于值与基于任务的延续

  • 组成的任务

    • when_all 功能

    • when_any 功能

  • 延迟执行任务

  • 任务组

  • task_group 与 structured_task_group 的比较

  • 示例

  • 可靠编程

使用 Lambda 表达式

Lambda 表达式是一种常用方法定义因为它们的简洁语法,由任务和任务组执行的工作。 这是在使用它们的一些提示:

  • 由于任务在后台线程通常以运行,请注意对象生存期,当获取在 lambda 表达式中的变量。 当您按值时获取变量,该变量的副本在 lambda 主体进行。 当获取引用时,不创建任何副本。 因此,请确保获得引用活动得比任务长使用它的生存期任何变量。

  • 通常,不要由该获取变量分配在堆栈。 这也意味着您不应获取在堆栈上分配对象的成员变量。

  • 是显式的有关可在 lambda 表达式获取帮助您标识的变量所按值获取的引用。 因此建议不要为 lambda 表达式使用 [=] 或 [&] 选项。

一个常见模式是在继续链的任务分配给变量,因此,另一个任务读取该变量。 因为,每个延续任务会保存该变量,不同的副本不能由值获取。 对于堆栈分配的变量,变量,因此可能不再有效,则也无法获取对的引用。

若要解决此问题,请使用智能指针,例如 std::shared_ptr,包装该变量并通过智能指针值。 通过这种方式,基础对象可分配到和读取和活动得比使用它的任务长。 请使用此技术,即使该变量是指针或引用计数的句柄(^)对于 Windows 运行时对象。 这是一个基本示例:

// 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
*/

有关 lambda 表达式的更多信息,请参见 在C++中Lambda表达式

[顶级]

任务选件类

可以使用 concurrency::task 选件控件或任务来设置相关的操作。 此组合模型由 延续的概念支持。 当条、 前面的任务,任务完成时,继续使代码执行。 前面的任务的结果将作为输入传递给一个或多个延续任务。 当一个前面的任务完成时,等待对对此的所有延续任务的计划执行。 每个延续任务受到前面的任务的结果的副本。 反过来,这些延续任务也可以是其他的延续前面的任务,从而创建任务的链接。 继续帮助您创建对它们中的特定依赖项任务的任意长链。 另外,任务可以参与、移除,在任务启动或以协作方式运行,则运行时。 有关此取消模型的更多信息,请参见 PPL 中的取消操作

task 是模板选件类。 该类型参数 T 是由任务产生的结果的类型。 如果任务不返回值,此类型可以是 void。 T 不能使用 const 修饰符。

在创建任务时,您提供执行任务正文的工作函数。 以 lambda 函数、函数指针或函数对象的形式,此工作函数是。 若要等待任务完成,而不获取该结果,请调用 concurrency::task::wait 方法。 task::wait 方法返回描述的 concurrency::task_status 值任务是否已完成或已取消。 捕获任务的结果,调用 concurrency::task::get 方法。 此方法调用 task::wait 等待任务完成,从而阻止当前执行线程,直到结果可用。

下面的示例演示如何创建任务,等待其结果并显示其值。 因为它们提供更为简洁的语法,本文档中的示例使用 lambda 函数。 但是,那么,当您使用任务时,也可以使用函数指针和函数对象。

// 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 功能使您能够使用 auto 关键字而不是声明该类型。 例如,请考虑创建和打印单位矩阵下面的代码:

// 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
*/

可以使用 create_task 函数创建等效的操作。

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;
});

如果在任务时,引发的异常在以后对 task::gettask::wait,或对基于任务的延续的运行时送的执行。 有关任务的异常处理机制的更多信息,请参见 并发运行时中的异常处理

对于使用 taskconcurrency::task_completion_event的示例,取消,请参见 演练:使用任务和 XML HTTP 请求 (IXHR2) 进行连接。 ( task_completion_event 选件类在本文档的稍后部分进行文档中描述)

提示

若要了解特定于 Windows 应用商店 app 的任务的详细信息,请参见 Asynchronous programming in C++用 C++ 为 Windows 应用商店应用程序创建异步操作

[顶级]

延续任务

在异步编程中,一个异步操作在完成时调用另一个操作并将数据传递到其中的情况非常常见。 传统上,使用回调方法,来完成的。 在并发运行时中,延续任务提供了相同功能。 延续任务(也简称为“延续”)是一个异步任务,由另一个任务(称为“前面的任务”)在完成时调用。 通过使用延续,您可以:

  • 将数据从前面的任务传递到延续。

  • 指定延续调用或不调用的精确条件。

  • 协作方式取消延续或者,在启动或之前,在运行时。

  • 提供有关应如何安排延续的提示。 (这适用于 Windows 应用商店 仅 app。 有关更多信息,请参见 用 C++ 为 Windows 应用商店应用程序创建异步操作。)

  • 从同一前面的任务中调用多个延续。

  • 调用一个延续,则中的全部或任何多线程的上级。

  • 将延续依次相连长度。

  • 使用延续来由前面的任务引发的异常。

当第一个任务完成后,您可以利用这些功能执行一个或多个任务。 例如,可以创建压缩文件的延续,在第一个任务读取它从磁盘之后。

下面的示例修改了前面一个使用 concurrency::task::then 方法计划打印前面的任务的值继续,则可用时。

// 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
*/

可以将和嵌套任务到任意长度。 任务还可以具有多个延续。 下面的示例演示增加前面的任务的值三次的基本继续链。

// 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
*/

继续也能返回另一个任务。 如果未取消,则此任务在后续继续之前执行。 此方法称为 异步展开。 异步展开,则可在后台若要执行额外工作,但是,不需要当前任务阻止当前线程。 (这是通用的。Windows 应用商店 app,继续在 UI 线程上运行)。 下面的示例显示了三个任务。 第一个任务返回在延续任务之前运行的其他任务。

// 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
*/

重要

当任务的继续返回类型 N时嵌套任务,发生的任务具有该类型 N,而不是 task<N>,并完成,则嵌套任务完成。换言之,继续展开嵌套任务。

[顶级]

基于值与基于任务的延续

将返回类型 T,测试 task 对象可以提供类型 Ttask<T> 的值传递到延续任务。 采用类型 T 的延续称为" 基于值的延续。 基于值的继续为计划执行,在前面的任务完成得不会出错时并不会被取消。 采用类型 task<T> 的延续,其参数称为" 基于任务的延续。 基于任务的继续为执行始终计划,在前面的任务完成时,因此,即使在前面的任务已取消或引发异常。 然后可以调用 task::get 获取前面的任务的结果。 如果前面的任务已取消,task::get 引发 concurrency::task_canceled。 如果前面的任务引发了异常,task::get 再次引发异常。 在延续前面的任务取消时,基于任务的延续未标记为已取消。

[顶级]

组成的任务

本节描述 concurrency::when_allconcurrency::when_any 功能,可帮助您编写多个任务实现常见模式。

Dd492427.collapse_all(zh-cn,VS.110).gifwhen_all 功能

when_all 功能会在完成之后组完整的任务的任务。 此函数返回在集合包含每个任务的结果 std::vector 对象。 下面的基本示例使用 when_all 创建表示其他三个任务的完成的任务。

// 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.
*/

备注

传递给 when_all 的任务必须是统一的。换言之,它们必须都返回相同类型。

如下面的示例所示,也可以使用 && 语法会在完成之后组完整的任务的任务。

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

在设置任务完成后,通常会使用 when_all 使用继续执行操作。 每个生成的 int 结果的以下示例修改了前面一个打印三个任务的和。

// 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.
*/

在此示例中,还可以指定 task<vector<int>> 导致基于任务的延续。

警告

如果在的所有任务组任务被取消或引发异常,when_all 立即完成和剩余不等待任务完成。如果引发了异常,则运行时再次引发异常,当您调用 task::gettask::waitwhen_all 返回的 task 对象。如果多个任务引发,运行时选择其中一个。因此,就会引发异常,请确保等待所有任务完成。

[顶级]

Dd492427.collapse_all(zh-cn,VS.110).gifwhen_any 功能

when_any 功能生成完成的任务,当中的第一个任务组任务完成。 此函数返回在包含完整的任务和该任务索引的结果集的 std::pair 对象。

when_any 函数特别用于以下情况:

  • 冗余操作。 考虑在许多方面可以执行的算法或"运算。 可以使用 when_any 功能选择最先完成取消剩余操作的操作。

  • 交错的操作。 可以开始任何必须完成和使用 when_any 功能的过程结果的多操作,在每个操作完成。 在一个操作完成后,可以开始一个或多个其他任务。

  • 将限制的操作。 可以使用 when_any 功能通过限制并发数扩展以上方案。

  • 过期的操作。 可以使用 when_any 功能选择一个或多个任务以及在特定时间后完成的任务之间。

when_all,通常会使用具有执行的 when_any 事件的延续,当第一个在一组任务完成。 下面的基本示例使用 when_any 创建完成的任务,当第一次其他三个任务完成。

// 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.
*/

在此示例中,还可以指定 task<pair<int, size_t>> 导致基于任务的延续。

备注

when_all,传递给 when_any 的任务都必须返回相同类型。

还可以使用 || 语法导致完成的任务,在的第一个任务组任务完成后,如下面的示例所示。

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

[顶级]

延迟执行任务

延迟任务的执行,直到满足条件有时需要的,或者启动任务以响应某个外部事件。 例如,在异步编程,您可能必须启动任务以响应 I/O 完成事件。

两种方式来实现此目的使用延续或启动一个任务和等待在事件在任务的工作函数中。 但是,但它不能使用以下技术之一的大小写。 例如,若要创建延续任务,您必须具有前面的任务。 但是,因此,如果没有前面的任务,您可以创建 任务完成事件 和最新链该完成事件。前面的任务,则用时。 此外,在中,因为正在等待的任务还阻止线程,则可以使用任务完成事件执行工作,在异步操作完成时,和自由线程。

concurrency::task_completion_event 选件类有助于简化任务这样的组合。 与 task 选件类,类型参数 T 是由任务产生的结果的类型。 如果任务不返回值,此类型可以是 void。 T 不能使用 const 修饰符。 通常,task_completion_event 对象提供给将通知它的线程或任务,则它的值用时。 同时,一个或多个任务设置为该事件的侦听器。 当事件设置时,侦听器任务完成,并且其计划继续运行。

对于使用 task_completion_event 实现任务完成的示例,在延迟,请参见 如何:创建在延迟一段时间后完成的任务之后。

[顶级]

任务组

“任务组”可组织任务的集合。 任务组会将任务推入工作窃取队列。 计划程序从该队列中移除任务,并用可用的计算资源执行任务。 在将任务添加到任务组之后,您可以等待所有任务完成或取消尚未开始的任务。

PPL 使用 concurrency::task_groupconcurrency::structured_task_group 选件类表示任务组和 concurrency::task_handle 选件类表示这些组中运行的任务。 task_handle 类封装执行工作的代码。 以 lambda 函数的形式,如 task 选件类,工作函数是,函数指针或函数对象。 通常不需要直接使用 task_handle 对象。 而是可以将工作函数传递给任务组,然后任务组会创建和管理 task_handle 对象。

PPL 将任务组分为以下两类:非结构化的任务组和结构化的任务组。 PPL 使用 task_group 选件类表示非结构化任务组和 structured_task_group 选件类表示结构化的任务组。

重要

PPL 还定义 concurrency::parallel_invoke 算法,使用 structured_task_group 选件类并行执行一组任务。由于 parallel_invoke 算法的语法比较简单,因此建议您尽可能使用该算法而非 structured_task_group 类。主题并行算法更加详细地介绍了parallel_invoke

当您需要同时执行多个独立任务并且必须等待所有任务完成才能继续时,可使用 parallel_invoke。 此方法通常称为 分叉和联接 并行度。 当您需要同时执行多个独立任务但希望稍后再等待任务完成时,可使用 task_group。 例如,您可以将任务添加到 task_group 对象中,然后在另一个函数中或从另一个线程等待任务完成。

任务组支持取消这一概念。 取消允许您向要取消整体操作的所有活动任务发送信号。 取消还阻止尚未开始的任务开始执行。 有关取消操作的更多信息,请参见 PPL 中的取消操作

运行时还提供异常处理模型,以允许您从任务引发异常并在等待关联任务组完成时处理该异常。 有关该异常处理模型的更多信息,请参见并发运行时中的异常处理

[顶级]

task_group 与 structured_task_group 的比较

虽然建议您使用 task_groupparallel_invoke 而不是 structured_task_group 选件类,该类具有,例如,要使用 structured_task_group的情况下,在并行算法执行可变数量任务或要求取消支持的写访问权。 本节说明 task_groupstructured_task_group 类之间的区别。

task_group 类是线程安全的。 因此您可以将任务添加到 task_group 对象从多个线程和等待或从多个线程取消 task_group 对象。 structured_task_group 对象的构造和析构必须使用相同的词法范围。 另外,对 structured_task_group 对象执行的所有操作必须在同一线程上进行。 此规则的例外情况是 concurrency::structured_task_group::Cancelconcurrency::structured_task_group::is_canceling 方法。 子任务随时可以调用这些方法来取消父任务组或检查取消。

在调用 concurrency::task_group::waitconcurrency::task_group::run_and_wait 方案后,可以运行在 task_group 对象的其他任务。 相反,在调用 concurrency::structured_task_group::waitconcurrency::structured_task_group::run_and_wait 方法后,将无法运行在 structured_task_group 对象的其他任务。

因为 structured_task_group 类不能跨线程进行同步,所以其执行开销比 task_group 类要小。 因此,如果您的问题不要求您利用多个线程计划工作并且您无法使用 parallel_invoke 算法,则 structured_task_group 类可以帮助您编写性能更佳的代码。

如果您在某个 structured_task_group 对象内使用另一个 structured_task_group 对象,则在外部对象完成之前,必须先完成并销毁内部对象。 task_group 类不要求在外部组完成之前先完成嵌套的任务组。

非结构化任务组和结构化任务组以不同方式使用任务句柄。 您可以直接将工作函数传递给 task_group 对象;task_group 对象将为您创建和管理任务句柄。 structured_task_group 类要求您管理每个任务的 task_handle 对象。 每个 task_handle 对象必须在其关联的 structured_task_group 对象的整个生命周期内保持有效。 如下面的基本示例所示,使用 concurrency::make_task 函数创建 task_handle 对象,例如:

// 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);
}

若要在具有可变数量任务的情况下管理任务句柄,请使用堆栈分配例程(例如 _malloca)或容器类(例如 std::vector)。

task_groupstructured_task_group 都支持取消操作。 有关取消操作的更多信息,请参见 PPL 中的取消操作

[顶级]

示例

下面的基本示例演示如何使用任务组。 本示例使用 parallel_invoke 算法并发执行两个任务。 每个任务都将子任务添加到 task_group 对象中。 请注意,task_group 类允许多个任务并发向其添加任务。

// 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();
}

此示例的示例输出如下:

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

因为 parallel_invoke 算法并发运行任务,所以输出消息的顺序可能会有所不同。

有关演示如何使用 parallel_invoke 算法的完整示例,请参见如何:使用 parallel_invoke 来编写并行排序运行时如何:使用 parallel_invoke 来执行并行操作。 有关使用 task_group 类实现异步功能的完整示例,请参见演练:实现 Future

[顶级]

可靠编程

确保您了解取消和异常处理的作用,当您使用任务、任务组和并行算法。 例如,在并行工作树中,取消的任务阻止子任务运行。 如果某个子任务执行的操作对应用程序很重要(例如释放资源),则这可能引发问题。 另外,如果子任务引发异常,该异常可能在对象析构函数中传播并导致您的应用程序出现未定义的行为。 有关说明这些问题的示例,请参见“并行模式库中的最佳做法”文档中的Understand how Cancellation and Exception Handling Affect Object Destruction一节。 有关 PPL 中取消和异常处理模型的更多信息,请参见PPL 中的取消操作并发运行时中的异常处理

[顶级]

相关主题

标题

描述

如何:使用 parallel_invoke 来编写并行排序运行时

演示如何使用 parallel_invoke 算法来提高 bitonic 排序算法的性能。

如何:使用 parallel_invoke 来执行并行操作

演示如何使用 parallel_invoke 算法来提高对共享数据源执行多项操作的程序的性能。

如何:创建在延迟一段时间后完成的任务

演示如何使用 taskcancellation_token_sourcecancellation_tokentask_completion_event 选件类创建在延迟后完成的任务。

演练:实现 Future

演示如何将并发运行时中的现有功能合并为某些效用更大的功能。

并行模式库 (PPL)

描述了为开发并发应用程序提供命令性编程模型的 PPL。

引用

task 类(并发运行时)

task_completion_event 类

when_all 函数

when_any 函数

task_group 类

parallel_invoke 函数

structured_task_group 类