并发运行时的概述

更新:2010 年 7 月

本文档提供并发运行时的概述, 介绍并发运行时的优势,什么情况下使用并发运行时,并发运行时的组件如何互相交互以及如何与操作系统和应用程序交互。

各节内容

本文档包含以下几节:

  • 并发运行时非常重要的原因

  • 体系结构

  • C++ Lambda 表达式

  • 要求

并发运行时非常重要的原因

并发运行时可为同时运行的应用程序和应用程序组件提供统一性和可预见性。 两个并发运行时优势的示例为协作任务计划和协作阻止。

并发运行时使用协作任务计划程序实现工作窃取算法以在计算资源之间有效地分配工作。 例如,假设应用程序具有两个线程,这两个线程均由同一运行时进行管理。 如果一个线程完成了其计划任务,则它可以卸载其他线程的工作。 此机制可平衡应用程序的总体工作负荷。

并发运行时还提供了使用协作阻止同步访问资源的同步基元。 例如,假设任务必须以独占方式访问共享资源。 通过以协作方式进行阻止,当第一个任务等待资源时,运行时可以使用剩余的量程执行另一个任务。 此机制将提升计算资源的最大使用率。

[转到页首]

体系结构

并发运行时分为以下四个组件:并行模式库 (PPL)、异步代理库、任务计划程序和资源管理器。 这些组件驻留在操作系统和应用程序之间。 下图演示了并发运行时组件在操作系统和应用程序中的交互方式:

并发运行时体系结构

并发运行时体系结构

并发运行时具有很好的可组合性,也就是说您可以组合现有功能来执行更多操作。 并发运行时利用较低级别的组件来提供多种功能,如并行算法。

并发运行时还提供了使用协作阻止同步访问资源的同步基元。 有关这些同步基元的更多信息,请参见同步数据结构

以下各节简要概述了每个组件提供的内容以及何时使用该组件。

并行模式库

并行模式库 (PPL) 为执行细粒度并行操作提供通用的容器和算法。 PPL 通过提供并行算法来启用命令性数据并行,这些并行算法将在计算资源之间分配数据集或数据集合计算工作。 它还通过提供任务对象来启用任务并行,这些任务对象将在计算资源之间分配多个独立的操作。

当具有可以从并行执行中受益的本地计算时,请使用并行模式库。 例如,您可以使用 Concurrency::parallel_for 算法转换现有的 for 循环以并行作用。

有关并行模式库的更多信息,请参见并行模式库 (PPL)

异步代理库

异步代理库(或只称作代理库)为粗粒度数据流和管道任务提供了基于参与者的编程模型和消息传递接口。 异步代理通过在其他组件等待数据时执行工作,使您能够卓有成效地利用延迟。

当您具有彼此异步通信的多个实体时,请使用代理库。 例如,您可以创建一个代理以从文件或网络连接中读取数据,然后使用消息传递接口将该数据发送到另一个代理。

有关代理库的更多信息,请参见异步代理库

任务计划程序

任务计划程序可在运行时计划和协调任务。 任务计划程序具有协作功能,它使用工作窃取算法实现处理资源的最大使用率。

并发运行时提供了默认计划程序,因此您无需管理基础结构详细信息。 但是,为了满足应用程序的质量要求,您也可以提供自己的计划策略或将特定计划程序与特定任务相关联。

有关任务计划程序的更多信息,请参见任务计划程序(并发运行时)

资源管理器

资源管理器的作用是管理诸如处理器和内存之类的计算资源。 如果工作负荷在运行时发生更改,资源管理器会将资源分配到效用最高的地方来响应工作负荷。

资源管理器将充当抽象的计算资源,主要与任务计划程序进行交互。 虽然可以使用资源管理器精细调整库和应用程序的性能,但通常应使用并行模式库、代理库和任务计划程序提供的功能。 当工作负荷发生变化时,这些库将使用资源管理器动态地重新平衡资源。

[转到页首]

C++ Lambda 表达式

并发运行时所定义的许多类型和算法以 C++ 模板形式实现。 其中的某些类型和算法以执行工作的例程作为参数。 此参数可以是 lambda 函数、函数对象或函数指针。 这些实体也称为“工作函数”或“工作例程”。

Lambda 表达式是重要的新 Visual C++ 语言功能,因为它们可提供简单的方式来定义并行处理的工作函数。 利用函数对象和函数指针,您可以将并发运行时与现有代码结合使用。 但是,建议您在编写新代码时使用 lambda 表达式,因为它们具有安全和工作效率方面的优势。

下面的示例在多个 Concurrency::parallel_for_each 算法的调用中比较 lambda 函数、函数对象和函数指针的语法。 每个对 parallel_for_each 的调用均使用不同的方法来计算 std::array 对象中每个元素的平方。

// comparing-work-functions.cpp
// compile with: /EHsc
#include <ppl.h>
#include <array>
#include <iostream>

using namespace Concurrency;
using namespace std;

// Function object (functor) class that computes the square of its input.
template<class Ty>
class SquareFunctor
{
public:
   void operator()(Ty& n) const
   {
      n *= n;
   }
};

// Function that computes the square of its input.
template<class Ty>
void square_function(Ty& n)
{
   n *= n;
}

int wmain()
{
   // Create an array object that contains 5 values.
   array<int, 5> values = { 1, 2, 3, 4, 5 };

   // Use a lambda function, a function object, and a function pointer to 
   // compute the square of each element of the array in parallel.

   // Use a lambda function to square each element.
   parallel_for_each(values.begin(), values.end(), [](int& n){n *= n;});

   // Use a function object (functor) to square each element.
   parallel_for_each(values.begin(), values.end(), SquareFunctor<int>());

   // Use a function pointer to square each element.
   parallel_for_each(values.begin(), values.end(), &square_function<int>);

   // Print each element of the array to the console.
   for_each(values.begin(), values.end(), [](int& n) { 
      wcout << n << endl;
   });
}

该示例产生下面的输出。

1
256
6561
65536
390625

有关 C++ 中 lambda 函数的更多信息,请参见 Lambda Expressions in C++

[转到页首]

要求

下表显示了与并发运行时的每个组件相关联的头文件:

组件

头文件

并行模式库 (PPL)

ppl.h

concurrent_queue.h

concurrent_vector.h

异步代理库

agents.h

任务计划程序

concrt.h

资源管理器

concrtrm.h

并发运行时是在并行命名空间中声明的。 Concurrency::details 命名空间支持并发运行时框架,不应在代码中直接使用。

并发运行时作为 C 运行库 (CRT) 的一部分提供。 有关如何生成使用 CRT 的应用程序的更多信息,请参见 C Run-Time Libraries

[转到页首]

修订记录

日期

修订记录

原因

2010 年 7 月

重新组织了内容。

信息补充。