1.join与detach的理解
1.std::thread 的c++对象创建完成以后,内部调用_beginthreadex创建底层线程,称之为_Thr。_Thr实际是包含 windows线程的句柄和id 的结构体。
2.调用join会使主线程阻塞,等待线程执行完成。如下:
join实际就是 waitforsingleobject + closehandle。
3.如果是调用detach,关注_Thrd_detach函数:
detach实际就是closehandle。
现在问题:
1.调用了join以后还能调用detach吗?
答:先看joinable()定义。
显然不能,因为两个都会closehandle,而handle close一次就ok了。同时c++对象也会设置_Thr = {},导致 joinable() = false。因此重复调用,非法,抛异常。
2.线程的执行函数实际执行完成了,再join/detach可以吗?
答:可以,没啥影响。线程执行函数体执行完了,join的wait+close会立即完成,detach也会立即完成。
3.为啥~thread()中会terminate?
答:如下,~thread定义 :
这是因为,~thread表示c++对象t的析构,此时如果joinable() = true,这表明之前没有调用过join和detach,此时_Thr中的句柄没有关闭。
假设不terminate():
1)改成detach()。这个c++对象都已经要析构了,内部线程继续执行???你要想这样,为啥不之前自己detach呢?
2)改成join()。。这,这析构还得等线程执行完,可能吗?
3)啥都不干,则句柄泄露。
而terminate()导致进程终止,防止句柄泄露。
2.关于传递参数的理解
这是c++并发编程中一个例子,书上这样说的:“In this case, it’s the pointer to the local variable buffer that’s passed through to the new thread and there’s a significant chance that the oops function will exit before the buffer has been converted to a std::string on the new thread, thus leading to undefined behavior.”。看完还是云里雾里,看了一下源码,个人理解:
std::thread中的构造函数首先会生成一个unique_ptr,_Decay_copied。调用_beginthreadex的时候,传递的内容是unique_ptr,但是unique_ptr中并非包含我们想象中的f的参数类型int和string,而是包含了一个int,一个char*。
因此,对于_beginthreadex来说,它大意了啊,它也不知道需要的是string,拿到了_Decay_copied这个unique_ptr,就返回线程句柄。
等到内部线程开始运行,对unique_ptr的指针解引用,才发现是char*,这时间,还没等const string& s(char*);外部的char*的字符可能就已经无效了。
总结与反思:
调用std::thread构造的时候,内部传参unique_ptr不会对传入的类型进行转换(从char*智能转成一个string)。因此解决方法有:
1.buffer是堆上的内容,这样直接传指针没问题
2.传f的定义类型const string&,甚至string都行,反正别指针