线程管控
1. 发起线程
当创建线程时,如果传入的是临时变量,而不是具名变量,那么调用构造函数的语法可能与函数声明相同,这就引起输入值二义性问题。此时,编译器会将其解释成函数声明,而不是定义对象。具体如下
class backgroud_task
{
public:
void operator()()const
{
do_something();
do_something_else();
}
};
std::thread my_thread(backgroud_task());//本意是发起新线程,但是却被解释成函数声明:函数名:my_thread,接受了一个参数,返回backgroud_task对象,该指针是对backgroud_task()不当解释,其原意是生成临时的匿名函数对象
解决办法:
//1 实例化一个对象
backgroud_task e;
std::thread my_thread(e);
//2 对右值临时变量增加一个括号
std::thread my_thread((backgroud_task()));
//3 使用大括号进行列表初始化
std::thread my_thread{backgroud_task()};
//4 使用lambda表达式
std::thread my_thread([]{
do_something();
do_something_else();});
线程一旦启动,我们就需要明确是等待它结束后再执行主程序(与之汇合),还是由它独自运行(与之分离),正常直至进程结束。
join() //等待线程结束后再执行主线程后续操作 (与之汇合)
detach() //将线程与主线程分离,主线线程对子线程不再控制,子线程将一直保持运行 (与之分离)
当传入参数为指针或引用,调用局部变量时,主线程结束,子线程还未结束,此时将会导致线程中指针或引用会访问已被销毁的变量,而导致程序运行崩溃。具体如下:
void function(int& a)
{
int b=a;
while(1)
{
do_something();
}
}
void oops()
{
int s=0;
std::thread my_thread(function(s));
my_thread.detach();
}
//此时oops()结束了生命周期,局部变量s也被销毁,但是线程function(),还在引用局部变量s
解决办法:
1.不使用指针或引用,直接进行数据复制 2. 采用join(),等待子线程先结束生命周期
2.出现异常情况下等待
如果线程启动后有异常抛出,而join()尚未执行,则该join()调用就会被略过。为了防止抛异常而导致程序终止,需要在异常中也调用join(),以避免意外生存期问题。具体如下:
void f()
{
int s=0
std::thread tt(function(s));
try
{
do_something_in_current_thread();
}
catch(...)
{
tt.join();//当执行function线程,出现异常,确保join(),函数顺利执行
throw;
}
tt.join();
}
3.向线程函数传递参数
每个线程具有内部存储空间,传递进来的参数会按照默认方式,先复制到此处,新线程才能直接访问他们,然后,这些副本将会被当成临时变量,以右值的形式传给新线程上可用的函数或调用对象,即便是引用也是如此。但是,如果参数是指针,并且指向自动变量,这样就会存在隐患。比如
void fun(int i,std::string const& s);
void oops(int param)
{
char buffer[1024];//
sprintf(buffer,"%i",param);
std::thread tt(fun,3,buffer);//本意:buffer会在新线程内转换成std::string对象,但是在此之前,oops()函数极有可能运行结束,局部变量buffer被销毁
t.datach();
}
解决办法:在传入之前就完成转换:
void fun(int i,std::string const& s);
void funf(int i,std::string& s);
void oops(int param)
{
char buffer[1024];//
sprintf(buffer,"%i",param);
std::thread tt(fun,3,std::string(buffer));
//同时,若想传入的是非const的引用,可通过如下
std::thread tt(funf,3,std::ref(std::string(buffer)));
t.datach();
}
4.其他
线程归属权移交通过std::move()函数来实现:
void function_1();
void function_2();
std::thread t1(function_1);//创建线程
std::thread t2(std::move(t1));//将t1线程归属权移交给t2
std::thread t1(function_2);//t1创建新的线程
获取线程数量:工具函数:std::thread::hardware_concurrency();返回值表示程序在各次运行中可真正并发的线程数量
获取线程id,可通过std::thread对象调用get_id()和调用std::tis_thread::get_id()获取当前线程id。