知识的学习在于点滴记录,坚持不懈;知识的学习要有深度和广度,不能只流于表面,坐井观天;知识要善于总结,不仅能够理解,更知道如何表达!
thread线程函数参数问题
从C++11开始,终于提供了语言级别的thread类库,从此可以通过C++语言编写多线程程序,做到一次编写,到处编译。thread对象可以传递普通函数、函数对象、lambda表达式等作为线程函数,使用起来非常方便。最近看到有人(tony)提出这样一个问题,代码片段:
#include <iostream>
#include <thread>
void handler1(int b) // 此处形参b,接收t1实参a的值,正确!!!
{
std::cout << "do handler1" << std::endl;
}
void handler2(int &b) // 此处形参b,想引用实参a,语法错误!!!为什么???
{
std::cout << "do handler2" << std::endl;
}
int main()
{
int a = 10;
std::thread t1(handler1, a);
t1.join();
// std::thread t2(handler2, a); 这里编译错误!!!
// t2.join();
}
tony定义了一个thread对象,绑定了handler2线程函数,handler2的参数是一个普通的左值引用变量int &b,为什么不能接收实参a? 也就是std::thread t2(handler2, a); 这句代码直接编译错误,在visual studio 2019上(编译默认使用C++ 14标准)错误信息如下:
可以看到vs报错“invoke未找到匹配的重载函数”,具体原理后面给大家详细解释。如果线程函数非得用int &b这样的左值引用来接收,使用的时候如下即可:
#include <iostream>
#include <thread>
void handler1(int b)
{
std::cout << "do handler1" << std::endl;
}
void handler2(int &b)
{
std::cout << "do handler2" << std::endl;
}
int main()
{
int a = 10;
std::thread t1(handler1, a);
t1.join();
std::thread t2(handler2, std::ref(a)); // 注意这里使用std::ref(a)即可!!!
t2.join();
}
tony的问题就是,他写的线程函数handler2,形参是int &b,为什么不能直接接收实参变量a,但是用std::ref(a)又可以了,底层原理是什么?
学习下thread类的源码
要从原理上解释上面的问题,我们需要看看thread类的源码,主要看从定义一个thread对象,到线程函数的调用,中间都执行了哪些代码操作,通过查看源码,看看造成上面问题的根本原因是什么?
从VS2019的C++类库中看thread的源码
拷贝thread的源码,我们实现一个自己的线程类Slthread,如下:
#include <iostream>
#include <tuple>
using namespace std;
class Slthread
{