导读
终于,在万众期待之下,C++11有了自己的线程库,实现了真正意义上的跨平台,今天在了解C++11线程库的同时,也来温习下POSIX线程。
POSIX线程
在C++11之前因为没有C++语言没有语言级别的线程库,所以在Linux上用的都是POSIX线程,POSIX的相关API大概如下:
1、创建线程创建线程的原型是:
其中是线程id,传入一个指针即可,如果创建成功会赋值为所创建线程的id。
是创建线程所需的参数,通过这些参数可以定制线程的属性,比如可以指定新建线程栈的大小、调度策略等,如果不需要传NULL即可。
第三个参数是一个函数指针,是线程创建成功所需要具体执行的任务函数。
第四个是线程携带参数,这个参数会在线程的函数指针执行时传递过去。
例如:
2、线程退出
正确的线程退出方式是在退出线程之后,再连接线程,因为如果在线程退出后不执行连接操作,线程的资源就不能被释放,也不能被复用,这就造成了资源的泄漏。所谓的连接操作就是在线程退出后使用即可。
更多其他的线程函数这里就不展开细说了,因为今天的主要内容还是C++11的线程知识。
更多关于POSIX线程的知识,笔者之前看过《Linux环境编程XXXX》(为避免广告就不写完整书名了)这本书讲得就不错。
C++11的线程
先来看一个C++11多线程的例子:
是不是比POSIX线程简洁得多?
1、线程分离如果在上面的例子中,我们将改成则表示我们不再接管这个线程了,相当于驻留在后台,完全被C++运行时库所接管了,当线程运行结束后,由C++运行库清理相关的线程资源。
需要说明的是上面的例子如果把改成的话很大可能就看不到输出了,但是这是正常的,而且一旦线程被之后就不能再被了。
对于一个线程我们可以通过函数判断是否可以join或者detach,如果一个线程已经被或者的,那么则会返回false。
2、线程传参
如果在线程执行的时候,我们想给线程传递参数那该怎么办呢?
如果需要给线程传递参数,那么我们只需要在线程执行函数后增加参数,然后将实参传入线程的构造函数中即可,例如:
既然涉及到传参,那么又回到了C++老生常谈的问题了,给线程传递参数,到底是按照值传参呢还是按照指针传参呢还是按照引用传参呢?
首先来看看以下的这个例子:
理论上来说上面的这个例子是有问题的,为什么呢?线程是在函数内开启的,但是函数返回时就销毁了栈变量thread,所以运行的时候我们是看到会报错的,以下是输出:
所以如果我们想要在一个函数内部开启一个线程,需要在线程的任务执行结束之前线程变量thread不要被销毁,需要使用堆指针或者局部变量的方式。
首先我们来测试一下值传递的方式:
下面是输出:
在上面的测试中我们发现如果是通过值传递的方式的话,如果需要使用多线程,则会经历三次拷贝的过程,这着实是有点性能的消耗了。
不是说通过引用可以减少拷贝吗?那我们来测试下引用的传递的情况是怎么样子的,还是以上的程序,我们修改一下函数
看看输出:
对比前面的值传递我们发现是少了一次拷贝,但是不是说值传递调用函数不拷贝吗?这里怎么还是进行了拷贝,而且是两次拷贝呢?其实说引用传递不拷贝是对于直接调用来说的,这里的多线程调用,系统并不会马上给你调用任务函数,而是内部经历了n次的封装才调用到开发者制定的任务函数,所谓的没有中间商赚差价也是相对而言的...
** 注意:虽然是使用了引用,但是内部还是经历了拷贝,那么如果希望通过传递引用,然后在线程函数内部修改值然后在线程函数外起作用这个想法就行不通了哦。**
再来看看指针的传递,我们修改下函数和
输出:
厉害了,居然没有发生对象的拷贝,虽然内部还是发生了指针的拷贝,但是这个损耗是可以忽略不计的,看来如果要给多线程传参还是得指针呀。
既然是指针,又要陷入谁维护,谁释放的漩涡了。。。
试下智能指针?还是修改下函数和
输出:
小结
1、给线程任务传递参数的话如果是一般的数据类型,比如int等的建议直接使用值传递即可。2、如果是传递复杂的类的参数的话,建议使用智能指针。
领取专属 10元无门槛券
私享最新 技术干货