主要有三部分组成,threadpool,scheduler,task。

三者关系如上图示,pplx只着重实现了task部分功能,scheduler跟threadpool只是简略实现。

threadpool主要依赖boost.asio达到跨平台的目标,cpprestsdk的 io操作同时也依赖这个threadpool。

pplx提供了两个版本的scheduler,分别是

linux_scheduler依赖boost.asio.threadpool。

window_schedule依赖win32 ThreadPool。

默认的scheduler只是简单地将work投递到threadpool进行分派。

用户可以根据自己需要,实现scheduler_interface,提供复杂的调度。

每个task关联着一个_Task_impl实现体,一个_TaskCollection_t(唤醒事件,后继任务队列,这个队列的任务之间的关系是并列的),还有一个_PPLTaskHandle代码执行单元。

task,并行执行的单位任务。通过scheduler将代码执行单元调度到线程去执行。

task提供类似activeobject模式的功能,可以看作是一个future,通过get()同步阻塞等待执行结果。

task提供拓扑模型,通过then()创建后续task,并作为后继执行任务。注意的是每个task可以接受不限数量的then(),这些后继任务之间并不串行。例 task().then().then()串行,(task1.then(), task1.then())并行。一个任务在执行完成时,会将结果传递给它的所有直接后继执行任务。

此外,task拓扑除了then()函数外,还可以在执行lambda中添加并行分支,然后可以在后继任务中同步这些分支。

也就是说后继任务同步原本task拓扑外的task拓扑才能继续执行。

 1 auto fork0 =
2 task([]()->task<void>{
3 auto fork1 =
4 task([]()->task<void>{
5 auto fork2 =
6 task([](){
7 // do your fork2 work
8
9 });
10 // do your fork1 work
11
12 return fork2;
13 }).then([](task<void>& frk2){ frk2.wait(); }); // will sync fork2
14 // do your fork0 work
15
16 return fork1;
17 }).then([](task<void>& frk1){ frk1.wait(); }); // will sync fork1
18 fork0.wait(); // sync fork1, fork2

上面的方式有一个问题,如果里层的fork先完成,将不要阻塞线程,但是外层fork先完成就不得不阻塞线程等待内层fork完成。

所以可以用when_all

task<task<void> >([]()->task<void> {
std::vector<task<void> > forks;
forks.push_back( task([]() { /* do fork0 work */ }) );
forks.push_back( task([]() { /* do fork1 work */ }) );
forks.push_back( task([]() { /* do fork2 work */ }) );
forks.push_back( task([]() { /* do fork3 work */ }) );
return when_all(std::begin(forks), std::end(forks));
}).then([](task<void> forks){
forks.wait();
}).wait();

通过上面的方式,也可以在lambda中,将其它task拓扑插入到你原来的task拓扑。

task结束,分两种情况,完成以及取消。取消执行,只能在执行代码时通过抛出异常,task并没有提供取消的接口。任务在执行过程中抛出的异常,就会被task捕捉,并暂存异常,然后取消执行。异常在wait()时重新抛出。下面的时序分析可以看到全过程 。

值得注意的是,PPL中task原本的设计是的有Async与Inline之分的。在_Task_impl_base::_Wait()有一小段注释说明

// If this task was created from a Windows Runtime async operation, do not attempt to inline it. The
// async operation will take place on a thread in the appropriate apartment Simply wait for the completed
// event to be set.

也就是task除了由scheduler调度到线程池分派执行,还可以强制在wait()函数内分派执行,后继task也不必再次调度而可以在当前线程继续分派执行。但是pplx没有实现

class _TaskCollectionImpl
{
...
void _Cancel()
{
// No cancellation support
} void _RunAndWait()
{
// No inlining support yet
_Wait();
}

下面是对task的时序分析。

开始的task创建_InitialTaskHandle, 一种只能用于始首的Handle执行单元。

通过then()添加的task,创建_ContinuationTaskHandle,(一种可以入链的后继执行单元),并暂存起来。

当一个任务在线程池中分派结束时,就会将所有通过then()添加到它结尾的后继任务一次过向scheduler调度出去。

任务只能通过抛出异常从而自己中止执行,task并暂存异常(及错误信息)。

后继任务被调度到线程池继续分派执行。

这里顺便讨论一个开销,在window版本中,每个task都有一个唤醒事件,使用事件内核对象,都要创建释放一个内核对象,在高并行任务时,可能会消耗过多内核对象,消耗句柄数。

并且continuation后继任务,在默认scheduler调度下,不会在同一线程中分派,所有后继任务都会简单投递到线程池。由线程池去决定分派的线程。所以由then()串行起来的任务可能会由不同的线程顺序分派,从而产生开销。因为pplx并没有实现 Inline功能,所有task都会视作Async重新调度到线程池。

浅析pplx库的设计与实现。的更多相关文章

  1. 【STM32H7教程】第12章 STM32H7的HAL库框架设计学习

    完整教程下载地址:http://forum.armfly.com/forum.php?mod=viewthread&tid=86980 第12章       STM32H7的HAL库框架设计学 ...

  2. 浅析DDD——领域驱动设计的理解

    浅析DDD--领域驱动设计的理解 我觉得领域驱动设计概念的提出,是为了更清晰的区分边界.这里的边界包括业务边界和功能的边界,每个边界都包含具体的领域对象,当业务和功能的领域对象一一对应上之后,业务的变 ...

  3. MySQL库表设计小技巧

    前言: 在我们项目开发中,数据库及表的设计可以说是非常重要,我遇到过很多库表设计比较杂乱的项目,像表名.字段名命名混乱.字段类型设计混乱等等,此类数据库后续极难维护与拓展.我一直相信只有优秀的库表设计 ...

  4. 多平台下Modbus通信协议库的设计(一)

    1.背景 1.1.范围 MODBUS 是 OSI 模型第 7 层上的应用层报文传输协议, 它在连接至不同类型总线或网络的设备之间提供客户机/服务器通信. 自从 1979 年出现工业串行链路的事实标准以 ...

  5. 浅析 jQuery 内部架构设计

    jQuery 对于大家而言并不陌生,因此关于它是什么以及它的作用,在这里我就不多言了,而本篇文章的目的是想通过对源码简单的分析来讨论 jQuery 的内部架构设计,以及 jQuery 是如何利用Jav ...

  6. Mysql配置优化,库表设计

    Mysql 服务器参数类型: 基于参数的作用域: 全局参数:set global autocommit = ON/OFF; 会话参数(会话参数不单独设置则会采用全局参数):set session au ...

  7. 浅析Python解释器的设计

    从现代编译器的角度看,解释器和编译器的边界已经相当的模糊.我们后面的讨论说到的编译器就是Python的解释器,没有特别说明的指的是CPython的实现. 内存管理(Memory Management) ...

  8. 浅析GDAL库C#版本支持中文路径问题(续)

    上篇博客中主要说了GDAL库C#版本中存在的问题,其表现形式主要是:"文件名中的汉字个数是偶数,完全没有影响,读取和创建都正常,如果文件名中的汉字个数是奇数,读取和创建都会报错." ...

  9. 浅析GDAL库C#版本支持中文路径问题

    GDAL库对于C#的支持问题还是蛮多的,对于中文路径的支持就是其中之一(另一个就是通过OGR库获取图形的坐标信息). 关于C#支持中文路径,看过我之前博客的应该都不陌生,如果使用的是我修改过的GDAL ...

随机推荐

  1. Multisim-74LS08\74LS02\74LS86逻辑功能仿真实验

    一. 实验目的 了解TTL门电路的外观封装.引脚分布和使用方法. 掌握数字电路试验台.万用表和示波器的使用方法. 掌握TTL与门.或非门和异或门的逻辑功能. 认识门电路对信号的控制作用. 二.实验内容 ...

  2. MySQL8多实例安装与mycat连接,最详细版本。

    [版权所有,转载请注明出处!违者必究!] 最近在搞mycat去实现主从库读写分离,所以博主就在自己的windows机器上进行了环境的搭建,在搭建MySQL多实例的时候还算顺利,就是mysql8和myc ...

  3. Python中对文件的读写

    读写文件前,我们先必须了解一下,在磁盘上读写文件的功能都是由操作系统提供的,现代操作系统不允许普通的程序直接操作磁盘. 读写文件就是请求操作系统打开一个文件对象(通常称为文件描述符),然后,通过操作系 ...

  4. 小智的糖果(Candy) 51nod 提高组试题

    luogu AC通道! (官方数据) 题目描述 小智家里来了很多的朋友,总共有N个人,站成一排,分别编号为0到N-1,小智要给他们分糖果.但 是有的朋友有一些特殊的要求,有的人要求他左右的两个人(左边 ...

  5. 【LINQ标准查询操作符总结】之聚合操符

    C#  中的LINQ 提供了两种操作方式,查询表达式和查询操作符,所有的查询表达式都有对应的查操作符类替代,查询表达式有点“类” SQL,在代码中写SQL,总觉得不够“优雅”,使用查询操作符就显得“优 ...

  6. 数据库之 MySQL --- 数据处理 之 多行子查询(五)

    ​ [1]需求: 谁的工资比Abel 高?方式一: 第一步线查出Abel共子 第二部进行过滤 SELECT salary FROM employees WHERE last_name = 'Abel' ...

  7. Java实现蓝桥杯凑算式(全排列)

    题目6.凑算式 凑算式 B DEF A + - + ------- = 10 C GHI (如果显示有问题,可以参见[图1.jpg]) 这个算式中AI代表19的数字,不同的字母代表不同的数字. 比如: ...

  8. Javascript实现万年历(日历表)

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  9. Java实现最长回文串

    1 问题描述 给定一个字符串,求它的最长回文子串的长度. 2 解决方案 2.1 中心扩展法 此处,首先枚举出回文串的中心位置,然后,再在该位置上分别向左和向右扩展,记录并更新得到的最长回文串的长度. ...

  10. 处理npm安装模块报错01

    报错:Error: EACCES: permission denied, mkdir '/usr/local/lib/node_modules/nodemon_tmp' 解决:sudo cnpm in ...