1 线程基本管控
每个C++程序都含有至少一个线程,即运行main()的线程,它由C++运行时系统启动。随后程序可以发起更多线程,它们以别的函数作为入口。这些新线程连同起始线程并发运行。当main()返回时,程序就会退出;同样,当入口函数返回时,对应的线程随之终结。如果借std::thread对象管控线程,即可选择等他结束。
1.1 发起线程
线程通过构造std::thread对象而启动,该对象指明线程要运行的任务。
void do_some_work();
std::thread myThread(do_some_work);
任何可调用类型都适用于std::thread。所以,作为代替,可以设计一个带有函数调用操作符的类(应当是下面的operator)
class background_task
{
public:void operator() () const{do_something();do_something_else();}
};background_task f;
std::thread my_thread(f);
f被复制到属于新线程的存储空间中,在那里被调用,由新线程执行。
1.1.1 与函数声明进行区分
如果传入std::thread的是临时变量,不是具名变量,那么调用构造函数的语法有可能与函数声明相同。这种情况,编译器会将其解释成函数声明。
声明为函数:函数名为my_thread,只接收一个参数,返回std::thread对象
std::thread my_thread(background_task());可以通过多用一对圆括号或使用新式的统一初始化语法
std::thread my_thread((background_task()));
std::thread my_thread{background_task()};还可以使用lambda表达式
std::thread my_thread([]{do_something();do_something_else();
});
在启动线程后,需要明确是要等待他结束(也就是汇合)还是任由他独立运行(也就是分离)。如果等到std::thread销毁的时候还没有决定好,那么std::thread的析构函数将调用std::terminate()终止整个程序。
如果选择了分离,且分离时新线程还未运行结束,那将继续运行,甚至在std::thread对象销毁很久之后依然运行,它只有最终从线程函数返回时才会结束运行。
假设程序不等待线程结束,那么在线程运行结束前,我们要保证它所访问的外部数据始终正确,有效。由于使用多线程,所以我们可能会经常面临对象生存期的问题。比如下面的案例:
struct func
{int& i;func(int& i_):i(i_){}void operator() (){for (unsigned j=0; j<1000000; ++j) {do_something(i); 隐患:可能访问悬空引用}}
};void oops()
{int some_local_state=0;func my_func(some_local_state);std::thread my_thread(my_func);my_thread.detach(); 不等待新线程结束新线程可能仍运行,而主线程的函数却已经结束
}