![](https://img-blog.csdnimg.cn/20201014180756927.png?x-oss-process=image/resize,m_fixed,h_64,w_64)
《白话C++》第12章 并发
文章平均质量分 57
《白话C++》第12章的内容
yanzhenxi
这个作者很懒,什么都没留下…
展开
-
《白话C++》第12章并发,Page577 12.5.3 “半自动”锁
全自动固然好,但在一些特定环境下,如果没办法全自动,那也不代表就马上变成“全手动”操作,这中间还有一个很科学的“半自动”过程。lock_guard <T>是封装互斥量得到一把全自动化的锁,变量定义时自动上锁,变量析构时自动解锁。到“原子操作”时,就会有该全自动锁无法适用的代码结构。lock定义时,自动为互斥量“_m”加锁,析构时自动解锁。接下来就是各种半自动功能所要解决的问题了。原创 2024-02-27 13:11:33 · 324 阅读 · 0 评论 -
《白话C++》第12章并发,Page574 12.5.2 互斥 修建代码“独木桥” 完整示例
⑦两个线程目标过程之间的数据同步(一个线程修改“_id”的值,另一线程反复读取该值,并以此确定是否结束线程)。本节给出有关IDCreator的完整代码。①通过宏标识只允许单一线程执行的代码;⑥并发环境下单例对象的使用;③两个互斥量各锁各的代码;②一个互斥量锁两处代码;④读写操作间的简单同步;原创 2024-02-27 10:59:59 · 336 阅读 · 0 评论 -
《白话C++》第12章并发,Page568 12.5.2 互斥 修建代码“独木桥”
要回答这个问题,得先清楚:谁和谁互斥?首先是抢着要通过GetID()的多个线程之间在互斥,其次是抢着要通过GetID()或者GetNewID()的多线程之间在互斥,因为两处用的是同一个互斥量“_m”,如图12-8所示。如图12-8所示,多个线程读取同一个值,这之间不需要互斥,但如果一个线程正好在通过GetNewID()修改“_id”的值,而另一个线程正在读取“_id”,那么并发冲突仍然会发生。这个宏就只是一个符号,没有任何实质内容,因此也不影响代码的任何逻辑,不过宏的名字有清楚的含义,这正是我们所需的。原创 2024-02-27 10:41:52 · 885 阅读 · 0 评论 -
《白话C++》第12章并发,Page565 12.5.2 互斥 并发冲突实例
main()函数不变,还是调用test(),重新编译后多次运行,应该可以看到重复的ID。如果着急看到效果,则可以在main()中写个循环,连续调用test()20次,并发冲突问题发生在这一行代码。解决办法自然是:不要让两个(或更多)线程同时执行“++ id”这个操作,换句话说,就是最多只让一个线程在执行“++id”这个操作。先将内存中现有“_id”的值复制到CPU寄存器,然后针对寄存器进行自增1操作,最后将自增得到的结果从寄存器复制到源内存。毫无问题,ID一个也不重复。原创 2024-02-26 21:23:33 · 360 阅读 · 0 评论 -
《白话C++》第12章并发,Page562 12.5.1 并发冲突分析
如果心里没有对并发下的共用资源时时刻刻留神,我们就非常容易认定002行一定会紧接着001行执行,忘了会有其他线程可能正好也在运行,并且正好也修改了COUNT的值,并且修改的时机正好位于001和002行之间。cout代表标准输出(通常是指显示屏),多个线程同时输出,就容易造成屏幕上一片混乱。不仅多数C++语句操作不是原子操作,甚至连单个汇编指令也大有可能不是原子操作,因为一个指令可能需要CPU花费多个时间周期执行。单线程环境下是0.2,多线程环境下有可能是其他值,不幸遇上COUNT为0,程序还会挂掉。原创 2024-02-26 18:24:51 · 355 阅读 · 0 评论 -
《白话C++》第12章并发,Page558 12.4 承诺 取线程的处理结果
请注意代码58行,detach()方法调用,使用“承诺”之后,最终是在future对象的get()时等待,因此可以不管线程对象是否执行完毕。编译,会报一大串错误内容,大意是找不到类型为“void(int, int, int)”的函数,原因是test()函数中,我们传递第三个入参r时,没有按calc的要求使用引用,std::promise<T> 就是T带上承诺以后的类型,依据需要,这里是int类型,当一切顺利,通过。28行,代用trd.join()之后才访问r,以确保线程目标过程确实完成对r的处理。原创 2024-02-26 16:15:21 · 309 阅读 · 0 评论 -
《白话C++》第12章并发,Page551 12.3.3线程剥离③ detach小例子
线程对象trd和线程剥离以后,被剥离的线程一直在后台不停地运行。直到main线程退出以后,被剥离的线程,才停止运行。当我们按下Ctrl + C,终止程序时,文本文档内的内容才停止。可以看到,文档内的内容一直再不停地增加。输入www.baidu.com,回车。原创 2024-02-26 12:46:09 · 379 阅读 · 0 评论 -
《白话C++》第12章并发,Page551 12.3.3线程剥离② 为什么要分手
子线程至少要执行10s,可是主线程只等5s就结束主函数,最终导致程序结束,谁来对子线程人生中的最后5s负责?当然不会,大家都是成年人,分手就各走各的。trd所代表的线程每隔1s就输出一个数字,连续输出10次(够无聊的)。结论:当主线程退出时,那些已经和相应对象“detach()”的实际线程,如果还在运行,就只能强制地,不体面地终止。线程对象创建出实际线程,然后就立马和后者宣告分手,彼此间再无瓜葛,多轻松。事情就这样,线程如果剥离,程序就管不了它。如果将28行,改为休眠11秒,则。主线程直接睡了5s,原创 2024-02-26 12:26:29 · 381 阅读 · 0 评论 -
话《C++》第12章并发,Page551 12.3.3线程剥离① 基本概念
1s之后,孤独而不知情的trd先是访问了线程ID,成功了,然后它在尝试合并线程,也成功了!线程ID就是线程的编号。接下来,我们将剥离trd对象和它所代表的实际线程后,然后再尝试通过get_id()方法,取线程ID,实际改动只是增加一行代码:28行,线程ID的类型是std::thred::id,标准卡为该类型重载了“<<”操作符,以方便输出,同时也提供相等、不等和大小比较等操作。说“没有绑定实际线程”也不太准确,因为我们知道trd曾经拥有一个实际线程,只是没能走到白头,半路detach()掉了。原创 2024-02-26 11:20:07 · 804 阅读 · 0 评论 -
《白话C++》第12章并发,Page543 12.3.2线程回汇合③ joinable
空的线程对象不能调用join(),已经完成join()调用的线程对象也不能重复调用。std::thread类提供joinable()方法,用于判断一个线程对象是否需要并且可以汇合。代码创建了一个线程对象,但没有为它指定任何目标任务,可以称之为“空线程对象”。空线程对象无事可做,因此不会创建实际线程,因此它不仅不需要,而且不可以调用join()方法。一旦你需要记住joinable()方法帮你搞清楚某个线程是否需要以及可以汇合,你一定是乱了。一个值得所有C++程序员经常浏览的好网站。原创 2024-02-26 09:46:30 · 359 阅读 · 0 评论 -
《白话C++》第12章并发,Page543 12.3.2线程回汇合②
一直以来举的多线程例子,都是父线程定义一个线程栈对象,从而创建子线程,接着子线程做点事,同时父线程做点事,最后父线程调用子线程的join()方法完成线程汇合。在复杂需求下,将一些线程都交个某个固定线程(几乎就是主线程)同一管理及归并这样的设计不不罕见,但一定要清楚,B线程创建的C线程却交给A线程归并,显然又多了一处线程间的数据同步,发生“时空错乱”问题的机会大为增长。当我们在A线程中定义新线程对象,并产生新线程B,我们就称A是B的父线程,B是A的子线程。17行,A线程(主线程)创建B线程对象,原创 2024-02-25 21:17:57 · 915 阅读 · 0 评论 -
《白话C++》第12章并发,Page543 12.3.2线程回汇合①
爸爸带三个小孩到儿童游乐场,小孩子们各玩各的,爸爸 无聊地守在游乐场门口等他们,这场景就是一个父线程等待三个子线程的对照。忘了等待子线程汇合,可能会乱了屏幕输出(哪怕我们加了锁),还会让一个进程直接中断(terminate),甚至惊动操作系统。三个子线程以及主线程的屏幕输出都是正常的,说明子线程确实执行完毕,程序却还是在退出时崩溃了,同样惊动到操作系统。child_c对象最先被释放,它在析构时发现自己没有调用过join(),这下它不干了。下面演示主线程通过休眠,等所有子线程结束,但就是不调用join();原创 2024-02-25 17:33:56 · 365 阅读 · 0 评论 -
白话《C++》第12章并发,Page539 异步绑定和任务包裹
不考虑例中选择“同步”执行的情况,将整件事简化为:在前面通过bind()绑定操作和数据,得到一个“包裹”对象,而后在需要时将该“包裹”传递给async()以实现异步调用。async()的情况是子弹上枪膛,把枪交给一个小弟,交待他执行“找敌人,瞄准射击”的操作,而我们保留关注操作结果的权利和途径。只是需要一个好多入参的函数而已,接下来造一个函数,用于绑定action和它的第1,3,4,5个入参:?可以看到,若是同步执行,则在当前线程内执行,若是异步执行,则新开一个线程,在新的线程内执行。原创 2024-02-25 16:36:02 · 900 阅读 · 0 评论 -
白话《C++》第12章并发,Page532 共享异步存根 使用shared_future的例子(洗车)
【代码】白话《C++》第12章并发,Page532 共享异步存根 使用shared_future的例子(洗车)原创 2024-02-25 11:20:22 · 290 阅读 · 0 评论 -
白话《C++》第12章并发,Page533 共享异步存根,复习随机数产生方法
可以看到,种子相同,产生的随机数序列是相同的。原创 2024-02-25 10:00:08 · 332 阅读 · 0 评论 -
白话《C++》第12章并发,Page532 共享异步存根
包括多个线程重复使用同一个future对象,也包括同一个线程重复调用一个future对象的get()操作。二是C++标准规定,调用同一个future的get()操作多次,结果将“不可预期”,当前GCC的实现是在第二次调用时抛出std::future_error异常。因为这二者都只负责等结果,不负责取结果,更不负责“催”结果。线程1判断目标过程未启动之后,刚刚要启动目标过程,线程2要来了,它也发现目标过程还没启动,于是也会进入if内部语句……多个线程上处理同一异步操作的结果。原创 2024-02-25 08:46:44 · 318 阅读 · 0 评论 -
白话《C++》第12章并发,Page530 避免时空错乱
async_1()肯定比async_2()先执行,但foo_1()缺不一定比foo_2()先执行。再看一个例子,015行,在计算N/N时,N的值是什么?如果异步调用DecN()来得及运行完毕,N是1;如果DecN()来不及运行,N是0,这个情况下,会发生“除零错”造成程序异常退出。考虑到异步操作需要启动线程的时间成本,因此N为1的比例极大,但就有可能在程序员正度婚假时,它发生了。有人说,我不会轻易使用去全局变量。原创 2024-02-24 23:59:56 · 416 阅读 · 1 评论 -
白话《C++》第12章并发,Page528 异步等待
C++11标准库提供的“async/future”,对“将某件事交给独立线程”这件事做了高度的封装,使得整个异步过程的实现,在明面上看不到“线程”的出现。那么,该封装会不会带来我们对异步过程的可控度变小呢?future类除了提供“get()(提取)”接口之外,还提供三个“等待”接口,分别是wait(),wait_for()和wait_until()。原创 2024-02-24 22:42:18 · 913 阅读 · 0 评论 -
白话C++》第12章并发,Page527 异步过程中异常
异步操作将在另一个线程中执行,如果在尝试启动(此时还在当前线程内)或启动之后的执行过程中(此时肯定在另一线程中)发生异常,该如何处理?原创 2024-02-24 21:32:49 · 350 阅读 · 0 评论 -
白话C++第12章并发,Page526 课堂作业,异步调用lambda
【代码】白话C++第12章并发,Page526 课堂作业,异步调用lambda。原创 2024-01-21 01:23:25 · 369 阅读 · 0 评论 -
白话C++》第12章并发,Page525 异步调用策略
调用无policy入参版本的async()函数,使用的是什么策略?可能立即启动异步操作,也可能拖延一阵再开始,当然也有可能拖到get()函数被调用后才启动。launch::defered则强制要求本次异步操作暂时不开始,直到遇上调用future对象的get(),这种效果相当于延缓了任务的执行(原来代码也有拖延症)。这个例子还有一个地方需要稍加注意:output函数有入参,无返回值,因此在调用async是,请牢记一点,仅在你的确不在意异步操作可能延迟的情况下,才使用无需指定策略的异步调用函数。原创 2024-02-24 20:15:18 · 316 阅读 · 0 评论 -
《白话C++》第12章并发,Page521 异步概念和示例
这行代码的解读是:我们真正想做的是调用calc_1()并得到它的结果,但我们不想等它执行,所以将它交给async()异步处理,async收到任务后给了我们一张收据,名为future_1,并且根据这张收条,将来可以得到int类型的数据(即calc_1()的执行结果)。更好的理解是将它当做“存根”或“收条”理解,我们让async()去做某件事,但它不能立即返回做此事的结果,只是给我们一张“收条”,未来我们可以依据它来取得真正的结果。当然,也可以引入boost相关的库,直接使用该库的cpu_timer工具类。原创 2024-02-24 13:41:49 · 360 阅读 · 0 评论 -
《白话C++》第12章并发,Page519 基础知识,查看cpu核数
(1)并发应用典型场景一:程序在等待外部输入,比如接受网络发来的消息,或者更直接一些,在等待用户的键盘输入,可是那个用户突然上厕所了。(2)并发应用典型场景二:电脑确实有多个CPU(包括多核),而程序当前又确实有两件完全不相关或仅是低关联度的工作需要做,那总不能就让一个CPU忙得半死,另一个在喝茶侃大山吧?(3)并发应用典型场景三:程序用户想做多事,并且觉得每件事优先级都很高,都不能搁置,于是程序用上多线程,每件事都干一点就切换到另一件。原创 2024-02-24 11:35:32 · 309 阅读 · 0 评论