本文主要内容包括:启动新线程;等待线程与分离线程;线程唯一标识符。
I、线程管理的基础
每个线程都有各自的入口函数。最简单情况下,任务通常是无参数无返回值的函数。这个函数在其所属的线程上运行,直到函数执行完毕,线程也就结束了。
1.1 启动线程
使用C++线程库启动线程,可以归结为构造std::thread
对象,std::thread
可以用可调用类型构造,将带有函数调用符类型的实例传入thread
类中,替换默认的构造函数,下面的代码使用函数对象来完成对thread
的构造:
class background_task {
public:
void operator()() const {
do_something();
do_something_else();
}
};
background_task f; //必须使用一个命名的变量
std:::thread my_thread(f);
这里需要注意一个问题,当把函数对象传入到线程的构造函数中时,需要避免一些语法解析问题:如果传递了一个临时变量,而不是一个命名的变量,C++编译器会将其解析为函数声明,而不是类型对象的定义:
如
std::thread my_thread(background_task());
这里相当于声明了一个名为my_thread
的函数,返回一个thread
对象,而非启动了一个线程。
可以通过以下两种方式解决这个问题:
std::thread my_thread((background_task()));
std::thread my_thread{background_task()}; //新统一的初始化语法
或者使用lambda表达式:
std::thread my_thread([] {
do_something();
});
1.2 join 与 detach
1、join
函数为等待线程完成,确保线程在函数完成前结束,并且只能对一个线程使用一次join
。
2、detach
会让线程在后台运行,这就意味着主线程不能与之产生直接交互。不会等待这个线程结束。
3、join
与detach
的区别可以理解为:在调用方法之后,主线程是否还有对线程的控制权。
II、向线程函数传递参数
#include<thread>
#include<iostream>
#include<string>
using namespace std;
void print(string& str) {
cout << str << endl;
}
int main() {
string str = "hello";
thread th1(print, ref(str)); //需要确保传入print的参数与其定义的引用一致
th1.join();
return 0;
}
III、转移线程所有权
执行线程的所有权可以在std::thread
实例中移动,下面是一个例子:
void some_function();
void some_other_function();
std::thread t1(some_function);
std::thread t2 = std::move(t1);
t1 = std::thread(some_other_function);
std::thread t3;
t3 = std::move(t2);
t1 = std::move(t3);
IV、识别线程
调用get_id()
方法可以直接获取线程id:
#include<thread>
#include<iostream>
#include<string>
using namespace std;
void print(string str) {
cout << str << endl;
}
int main() {
cout << this_thread::get_id() << endl; //通过this_thread获得当前线程标识
thread th1(print, "id");
cout << th1.get_id() << endl; //通过get_id识别线程
th1.join();
return 0;
}
【参考】
[1] 《C++ Concurrency In Action》
网友评论