《白话C++》第12章并发,Page543 12.3.2线程回汇合②

2.谁等谁

线程不能自己等自己。当我们说线程“不能自己等自己”时,这里的“自己”指的是实际线程,而不是C++代码中std::thread对象。

完整的表达应该是“不能调用当前线程对应的线程对象的join()方法”。

为了更好地理解“线程对象”与“线程对象对应的真实线程”之间的关系,我们把“在父线程中定义线程对象,创建市级线程,再通过线程对象调用join()方法,以便等待实际线程执行完毕”这一过程,画张图表示:

从图中可以看出,定义trd变量之时,所处的环境是父线程;调用trd.join()的也是父线程。显然,trd这个线程对象没有在它所对应的实际线程中调用join()方法。

“花样作死”

先看花样:

花样玩法是:为了创建trd对象,我们为它提供一个Lambda表达式作为它的构造入参,但这个Lambda表达式的实现,又需要依赖于“捕获”到trd变量。概念上听着显示“循环”依赖的谬论,技术上实现到是不难:因为Lambda实际捕获的“&trd”,只是一个地址。新线程会输出trd的线程ID,并断言它就是当前线程的ID,这当然正确。

接着开始“作死”,将trd.join()从父线程处挪入子线程的执行体:

一直以来举的多线程例子,都是父线程定义一个线程栈对象,从而创建子线程,接着子线程做点事,同时父线程做点事,最后父线程调用子线程的join()方法完成线程汇合。甚至这其中的“父线程”基本就是主线程,并且基本就是在main()函数中开展一切。

实际项目中情况往往会复杂一些,有时候会以new的方式创建堆中的std::trhread对象,或者采用shared_ptr <thread> 等智能指针管理,而指针非常方便传递,很可能被传递到对象实际对应的线程中去。

【小提示】:std::thread析构函数为何不尝试调用join()

std::thread类被设计为在析构过程中不去尝试调用join(),一个原因当然是避免在调用join()这件事上掺和一把,更乱了程序员;另一个更主要的原因是,join()(汇合)操作不是严格意义上的资源回收,不应将它归到RAII的设计范畴里。

当我们在A线程中定义新线程对象,并产生新线程B,我们就称A是B的父线程,B是A的子线程。

不过,子线程并不一定需要由父线程汇合。比如,让A线程(主线程)创建B线程、后者再创建C线程,但最终B、C线程都在主线程中汇合,如图所示:

在复杂需求下,将一些线程都交个某个固定线程(几乎就是主线程)同一管理及归并这样的设计不不罕见,但一定要清楚,B线程创建的C线程却交给A线程归并,显然又多了一处线程间的数据同步,发生“时空错乱”问题的机会大为增长。

12行,代码定义了一个全局变量  thread_vector  用于存放线程对象的裸指针(更好的做法是使用shared_ptr)。

17行,A线程(主线程)创建B线程对象,

20行B线程又创建C线程对象指针,

25行,将它加入到thread_vector中,

28行,最后循环汇合并释放该容器内的线程。

这种做法很常见,稍作封装,就可以实现将所有需要在主线程做善后的线程,都加入该容器,最终全部由主线程处理。

但上面的代码有个大问题:28行有极大的概率要比025行先执行,执行效果如下,可见25行还未执行,容器还是空的

解决方案如下:

添加29行代码,28行是土办法

改进后运行效果:

【课堂作业】:改进主线程管理其他线程的例程

(1)解决主线程遍历容器时,容器还是空的问题

(2)将“thread * ”改为使用 shared_ptr <thread>;

(3)B的目标过程执行内容,改成循环创建10个C线程对象,cout改为使用COutWithMutx,输出内容需增加每个线程循环创建时的时序;

运行效果:这说明,45行,创建了10个C线程,这10个C线程,谁先创建完,不一定

C++语言经常被用于实现后台服务程序,这类程序往往1年365天不能下线(停止运行),程序的时间线近乎永恒。此时,线程相关的“时空错乱”问题往往变得更加隐秘,不易触发;另一方面,在主线程或某一专门线程中管理当前进程中的所有线程(或至少是由我们一手创建的线程)这一设计,在进程的监控维护、调优排错等方面,实用性更强了。

初学者还是老老实实按“父线程创建子线程,父线程负责汇合子线程”的思路写代码

如果真的不想管子线程,我们很快要讲到一个正招:线程“detach(剥离)”方法。该方法让线程对象和它代表的实际线程挥手一别,相忘江湖。当然如果就是想定义一个线程对象之后就不再理它,还有个“一不做二不休”的歪招。

  • 23
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值