线程的启动
线程在以初始函数(新线程的入口)构造std::thread对象时启动,换句话说,合理的构造std::thread即启动线程
(注意:以无参构造函数构造的std::thread对象并不启动线程)
构造std::thread对象的三种方式
使用普通函数构造
void do_some_work();
std::thread my_thread(do_some_work);
使用可调用对象构造
class background_task
{
public:
void operator()() const
{
do_something();
do_something_else();
}
};
background_task f;
std::thread my_thread(f);
使用类成员函数与类对象构造
class Test
{
public:
void testFunc();
};
Test myTest;
std::thread t(&Test::testFunc, &myTest);
注意:
1、使用以函数对象构造std::thread对象的方式时,需要注意避免直接传递临时对象(直接调用类的构造函数时产生的对象),因为当传递临时对象时,C++编译器会将其解析为函数声明,而不是类对象的定义,因此不会启动线程
std::thread my_thread(background_task()); //被视为声明一个参数为函数指针且返回值为std::thread对象的函数
解决方案
1)、使用命名对象而不是直接传递临时对象
background_task f;
std::thread my_thread(f);
2)、使用多组括号
std::thread my_thread( (background_task()) ); //额外的一对括号
3)、使用C++11新特性,同一初始化语法
std::thread my_thread{ background_task() }; //大括号
4)、使用C++11新特性lambda表达式
std::thread my_thread( []{ //捕获列表为空
do_something(); //参数列表与返回值类型为空
do_something_else(); //函数体为两个函数的调用
} );
Lambda表达式表示一个可调用的代码单元(像函数对象一样的可调用对象),可以将其理解为未命名的内联函数
线程启动之后
线程在启动之后我们可以对其选择两种操作:
1、使用join()明确等待线程结束(加入式)(有点像pthread_join())
2、使用detach()让其自主运行(分离式)(有点像pthread_detach())
注意
1、在std::thread对象析构之前,无论是分离还是加入,必须为其选择一种操作,否则程序就会终止(std::thread对象的析构函数会调用std::terminate()函数)
2、如果线程已经结束(指线程入口函数全部执行完毕),再去分离它,线程(线程入口函数)可能会在std::thread对象销毁后继续运行下去(测试发现并不是这样,或许没碰到我期望的可能吧