主要有三部分组成,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. Javascript输入输出语句

    方法 说明 归属 alert(msg) 浏览器弹出警示框 浏览器 console.log(msg) 浏览器控制台打印输出信息 浏览器 prompt(info) 浏览器弹出输入框,用户可以输入 浏览器 ...

  2. Life In Changsha College- SQA计划与验收测试规程

    本次任务 以前面小组完成的SCRUM设计任务作为背景,拟制软件开发任务的虚拟SQA计划. 编制前面小组完成的SCRUM设计任务的验收测试规程. SQA计划 验收测试规程 测试分析:工程完成的功能包括登 ...

  3. [JavaWeb基础] 032.第三方插件pinyin4j的使用

    突然发现了一个比较新奇的插件,就是可以把我们输入的汉字,输出它所有的拼音的jar包.下面以代码的形式简单的介绍下这个插件 package com.babybus.sdteam.pinyin4j; im ...

  4. [Objective-C] 015_Delegate(委托代理)

    Delegate在iOS开发中随处可见,Delegate是一种功能强大的软件架构设计理念,它的功能是程序中一个对象代表另一个对象,或者一个对象与另外一个对象协同工作(如小明喜欢一个女孩如花,却苦于没有 ...

  5. 完美解决报错Failed to convert value of type 'java.lang.String' to required type 'java.util.Date'

    Failed to convert value of type 'java.lang.String' to required type 'java.util.Date' 首先这个错误的意思是 前台页面 ...

  6. ZooKeeper未授权漏洞

    ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,它是集群的管理者,监视着集群中各个节点的状态根据节点提交的反馈进行下一步合理操作.最终, ...

  7. 运用惰性删除和定时删除实现可过期的localStorage缓存

    localStorage简介 使用localStorage可以在浏览器中存储键值对的数据.经常被和localStorage一并提及的是sessionStorage,它们都可以在当浏览器中存储键值对的数 ...

  8. 分布式 ID 的 9 种生成方式

    为什么要用分布式ID? 在说分布式ID的具体实现之前,我们来简单分析一下为什么用分布式ID?分布式ID应该满足哪些特征? 什么是分布式ID? 拿MySQL数据库举个栗子: 在我们业务数据量不大的时候, ...

  9. Rocket - tilelink - Xbar

    https://mp.weixin.qq.com/s/UXFHYEQaYotWNEhshro68Q   简单介绍Xbar的实现.   ​​   1. 基本介绍   用于为Xbar的输入和输出连接生成内 ...

  10. Rocket - tilelink - Filter

    https://mp.weixin.qq.com/s/6XX0CZHoDotIgLbNDSIUog   简单介绍Filter的实现.   ​​   1. 基本介绍   使用过滤器过滤掉client和m ...