首页 > 系统相关 >windows C++-并行编程-PPL任务并行(一)

windows C++-并行编程-PPL任务并行(一)

时间:2024-09-15 14:56:04浏览次数:11  
标签:task matrixRow matrix windows 并行 C++ 任务 concurrency 使用

在并发运行时中,任务是执行特定作业并通常与其他任务并行运行的工作单元。 任务可以分解为组织成任务组的其他更细化的任务。

编写异步代码,并希望在异步操作完成之后进行某种操作时,可使用任务。 例如,可以使用一个任务以异步方式从文件读取,然后使用另一个任务(延续任务,本文档稍后会对此进行说明)在数据可用之后处理数据。 相反,可以使用任务组将并行工作分解成较小的各部分。 例如,假设你有一个将剩余工作划分为两个分区的递归算法。 可以使用任务组并发运行这两个分区,然后等待划分的工作完成。

要将相同例程并行应用于集合的每个元素时,可使用并行算法(如 concurrency::parallel_for),而不是任务或任务组。

要点
  • 通过引用将变量传递到 Lambda 表达式时,必须保证该变量的生存期在任务完成之前一直保持;
  • 编写异步代码时可使用任务(concurrency::task 类)。 任务类使用 Windows 线程池作为其计划程序中,而不是并发运行时;
  • 要将并行工作分解成较小的各部分,然后等待这些较小部分完成时,可使用任务组(concurrency::task_group 类或 concurrency::parallel_invoke 算法);
  • 使用 concurrency::task::then 方法可创建延续。 延续是在另一个任务完成之后异步运行的任务。 可以连接任意数量的延续以形成异步工作链;
  • 基于任务的延续始终计划为在先行任务完成时执行,甚至是在先行任务取消或引发异常时执行;
  • 使用 concurrency::when_all 可创建在任务集的所有成员都完成之后完成的任务。 使用 concurrency::when_any 可创建在任务集的一个成员完成之后完成的任务;
  • 任务和任务组可以参与并行模式库 (PPL) 取消机制;
使用 Lambda 表达式

由于其语法简洁,因此 lambda 表达式是定义由任务和任务组执行的工作的常用方法。 下面是一些使用提示:

  • 因为任务通常在后台线程上运行,所以在 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
*/
task 类

可以使用 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::get 或 task::wait 或是基于任务的延续的后续调用中封送该异常。

标签:task,matrixRow,matrix,windows,并行,C++,任务,concurrency,使用
From: https://blog.csdn.net/m0_72813396/article/details/141576064

相关文章

  • c++修炼之路之AVL树与红黑树
    目录一:AVL树1.AVL树的概念2.AVL树插入数据后平衡因子及更新的情况3.AVL树节点的定义 4.AVL树的插入及旋转 二:红黑树 1.红黑树的概念及性质2.红黑树节点的定义3.红黑树的插入操作情况 4.红黑树与AVL树的比较 接下来的日子会顺顺利利,万事胜意,生活明朗---------......
  • Windows基线检测及加固
    一、身份鉴别1.口令复杂度策略:控制面板->管理工具->本地安全策略->账户策略->密码策略->密码必须符合复杂性要求->已启用【Windows-sever-2008默认开启】查看是否强制密码历史设置为大于等于标准值2.口令最长生长周期策略:控制面板->管理工具->本地安全策略->账户策略->密......
  • 南沙C++信奥老师解一本通题: 1161:转进制
    ​ 题目描述】用递归算法将一个十进制数X转换成任意进制数M(M≤16)。【输入】一行两个数,第一个十进制数X,第二个为进制M。【输出】输出结果。【输入样例】3116{将十进制31转化为十六进制数}【输出样例】1F#include<iostream>usingnamespacestd;intx,m;void......
  • windows server2012 配制nginx安装为服务的时候,直接跳要安装.net框架,用自动的安装,直接
    1、上一个已成功在安装过程中的图:2、之前安装过程中错误的图:3、离线安装解决:下载.netframework3.5,然后解压后,选择指定备用源路径,然后选择.net安装包所在目录:只要指定上面全路径就可以,要看到先多目录。4、再次安装nginx服务成功:这样服务就安装成功了。参考:Win......
  • 国产RAID卡2230-10i windows&Linux操作系统安装指导
    环境准备:1.准备2个U盘。一个刻录系统,一个装载驱动2.需保持CSM为UEFI状态和PCIEDEVICESLIST 下2230-10i的卡为UEFI状态,如图:环境排查:由于......
  • C++ 定义静态成员 static 关键字不能在定义出重复出现
    定义静态成员和其他的成员函数一样,我们既可以在类的内部也可以在类的外部定义静态成员函数。当在类的外部定义静态成员时,不能重复static关键字,该关键字只出现在类内部的声明语句:voidAccount::rate(doublenewRate){interestRate=newRate;}Note:和类的所有成员一样,当我......
  • C++ 3/5 法则相关
    拷贝构造函数拷贝构造函数的第一个参数必须是一个引用类型。虽然我们可以定义一个接受非const引用的拷贝构造函数,但此参数几乎总是一个const的引用。拷贝构造函数在几种情况下都会被隐式地使用。因此,拷贝构造函数通常不应该是explicit的(参见7.5.4节,第265页)。一般情况,......
  • C++ Primer Plus 第六版中文版(上)
    参考资料:《C++PrimerPlus第六版中文版》笔记作者:Mr.Crocodile欢迎转载文章目录开始学习C++头文件命名规定名称空间`cout`、`cin`函数处理数据简单变量变量命名规则整型运算符`sizeof`和头文件climitsclimits中的符号常量变量初始化整型字面量整型字面量后缀char......
  • C++编译器的那些事
    接上文OK!Rightnow!  Let's go!C++编译器是如何工作的?C++编译器实际负责什么?我们把C++代码写成文本。就是这样,他只是一个文本文件,然后我们需要一些将文本转换为实际应用程序的方法,我们的计算机可以运行。从文本形式到实际可执行的二进制文件,我们基本上有两个主要......
  • 哈?Dev C++ 支持代码智能补全啦?
    众所周不知,我是一名VS的用户,其实也用过其他的很多的C++编译器。印象最深的,还是DevC++。因为它是以一个个的.cpp文件为单位,可以直接编译运行,非常舒畅,不像VS那样,是以一个个项目为单位。而直到有一次,我原先安装的DevC++被我搞坏了,于是在本地存的一个安装包中随便找了一个......