线程管理
线程管理
std::thread可以通过有函数操作符类型的实例进行构造。
#include <thread>
class Task {
public:
void operator()() const {
dosomething();
}
};
Task task;
std::thread mythread(task);
使用一个有括号操作符函数的自定义类作为线程对象的构造参数,构造出来的线程对象会调用这个类的括号操作符函数,也就是说,task对象会被复制到mythread的线程空间中,函数对象的执行和调用都是在t的线程空间中完成。
当把函数对象作为构造参数传入线程对象时,存在一个语法解析上的问题。如果你传递了一个临时变量,而不是一个命名变量,C++解析器会将其解析成函数声明,而不是类型对象的定义。
std::thread mythread(Task());
这样相当于声明了一个函数名为mythread,有一个函数指针参数(指向一个没有入参,返回值是Task对象), 返回值是std::thread对象。
解决办法是再加一个括号,或者使用初始化语法。使用lambda表达式也可。
std::thread mythread((Task()));
std::thread mythread({Task()});
使用join()等待线程执行完成。调用join()后,会清理线程相关的内存,所以每个线程对象只能调用一次,之后就不可以再调用。调用joinable()可以查看当前线程对象是否还可以join。
使用detach()分离线程,分离线程后当前线程将不再等待分离出的线程是否执行完成(例如在main函数中分离线程,并直接退出main函数,在分离的线程中的cout就无法输出了)
如果在
join()被调用前有异常被抛出,如果没有捕获处理的话,会导致执行join()的部分被跳过,线程在程序完全结束前永远无法被释放,从而导致线程对象的生命周期出现问题。
参数传递
向线程对象传递参数只需要在函数对象后面继续追加参数就好了,但是需要注意的是,后面附加的参数都将会是以拷贝的形式(左值或右值)传递到线程的内存空间里,无论线程对象绑定的函数入参是否是引用。
void fun(std::string& s);
std::thread my_thread(fun, "hello, world");
线程对象的构造参数追加的是一个字面量字符串,通过这个字符串构造了一个string的临时变量传入到了my_thread线程的内部。
这样会存在一个问题。
void fun(std::string& s) {
do_something();
}
void make_thread(int num) {
char buffer[1024]{"maybe undefined"};
std::thread problematic_thread(fun, buffer);
problematic_thread.detach();
}
在构造线程的函数中构造了一个线程对象,绑定的执行函数入参是一个string类型,实际传入的是一个字符串,会调用string类的构造,生成一个临时的string对象传给要执行的函数中,逻辑上没有问题的。
但是,再仔细考虑一下problematic_thread做了什么:
- 拷贝字符串指针到内存空间。
- 通过字符串指针构造一个临时string对象,再传递给绑定的函数。
由于传入的指针指向的是函数内的局部变量,在这两步之间就可能出现问题了,在将指针拷贝到线程空间后,原有的局部变量可能被释放了,然后线程对象又执行了string的构造,这就造成了未定义的行为。
有效的解决方法是明确传入的类型。
void make_thread(int num) {
char buffer[1024]{"maybe undefined"};
std::thread safe_thread(fun, std::string(buffer));
problematic_thread.detach();
}
传递引用
如果要向线程中传递引用,需要使用std::ref()显式地将参数指定为引用形式。这和标准库中的std::bind()使用类似。
转移线程所有权
类似于std::unique_ptr,线程对象也应该是独有的,仅支持通过std::move()转移所有权。
机器的线程数量
标准库中提供了std::thread::hardware_concurrency()接口,用于获取当前机器的可并发的线程数量,一般等于cpu的核心数量。返回值只是一个数值,当无法获取时,会返回0。
线程标识
线程的标识是一个std::thread::id类型,可以通过get_id()来获取。
- 如果是线程对象调用,
onethread.get_id(),会返回该线程对象的标识,对象如果没关联任何执行线程,会返回std::thread::type的默认构造值,标识“没有线程”。 - 还可以直接在当前线程使用
std::this_thread::get_id(),可以获得当前线程的标识。

浙公网安备 33010602011771号