目录
4.在运行时选择线程数量【std::thread::hardware_concurrency()】
5.3.2 std::this_thread::get_id()
1. 线程的基本管控
每个C++程序至少有一个主线程,即运行main()的线程。
1.1 发起线程
class task
{
public:
void operator()() const
{
do_something();
do_other_something();
}
};
task f;
std::thread my_thread(f);
1.1.1 线程的声明
任何可调用对象(如函数指针、函数对象、lambda等,即能进行函数调用操作的对象)都适用于std::thread。 所以。在这里我们写了一个task类,重载运算符"()",将其写为能进行函数调用操作的类,生成其对象,初始化线程my_thread。f会被复制到属于新线程的存储空间中,并在那里被调用,由新线程执行。这里要注意一个问题,只要这个C++语句有可能被解释为函数声明,那么编译器就会把它解释为函数声明。如:
std::thread my_thread(func());
[函数返回类型] [函数名][调用参数]
这种被解释为函数申明的就会报错,解决办法:
std::thread my_thread((func())); //使用func()函数返回的临时对象
std::thread my_thread{func()}; //列表初始化
1.1.2 汇合或分离线程
在线程对象销毁前,必须决定好,是汇合线程(join)还是分离线程(detach),否则在线程对象销毁时,将会在析构时,调用std::terminate()函数终止整个程序。
1.1.3 线程的启动
线程一旦初始化,就开始启动。 我们只需要在线程对象销毁前,决定好,就行了,不用管线程是在join或detach之前结束,还是在detach之后结束。
1.1.4 线程的运行
在线程运行中,注意未定义行为的发生。如:
struct func
{
int& i;
func(int *i_):i(i_){}
void operator()()
{
for(int i=0;i<1000000;++i)
do_something(i);
}
}
void oops()
{
int k=0;
func my_func(k);
std::thread my_thread(my_func);
my_thread.detach();
}
func类里,i用的是引用类型,当初始化my_func时,i则初始化为临时变量k的引用,当my_thread线程开始后,结束前,oops函数就已经结束了,临时变量k被销毁,则线程里继续访问k的引用时,将发生未定义行为。避免此问题的发生:1.线程完全自含,所有调用的变量都由线程本身生成,2.汇合线程(join),等待此线程结束再继续执行。
1.2 等待线程完成(join)
汇合线程(join):
join()简单粗暴,会一直等待线程结束。当join()执行完成,线程结束后,会把线程的所有资源释放掉,std::thread关联的对象,将不再有效。所以,join()有且只能调用一次,可使用joinable()进行判断,没有调用过join为true,否则则为false。
if(my_thread.joinable())
{
my_thread.join();
}