自c++11版本后,标准库也提供了对线程的支持。虽然大多场合还是使用其他的三方线程库,如:boost::thread, QThread等,但是学习下还是有必要的。
std::thread类即创建子线程的类,定义于头文件thread中。
std::thread类仅不到十个公开成员函数,同时无法进行拷贝,只能使用移动构造和赋值转移所有权。
#include
[static]
unsigned int std::thread::hardware_concurrency();
// 返回硬件支持的线程数目,失败返回0
#include
#include
int main()
{
std::cout << "CPU是" << std::thread::hardware_concurrency() << "线程" << std::endl;
return 0;
}
#include
[public]
template<typename _Callable, typename... _Args>
std::thread::thread(_Callable&& __f, _Args&&... __args);
// 构造函数之一, 传一个可以调用的函数或者对象作为任务, 有子线程来处理运行这个任务。
// 未调用此构造函数的线程是无法执行的,称为空任务线程。
// 需要明确的是构造完成后,子线程即开始执行,而不是等join(结合)或者detach(分离)调用才执行
bool std::thread::joinable() const;
// 未被结合或者分离的非空任务线程会返回true, 否则返回false。
std::thread::id std::thread::get_id() const;
// 未被结合或者分离的非空任务线程会返回非0的整数作为子线程的标识符
#include
#include
void func(int a)
{
std::cout << "This function is callable: " << a << std::endl;
}
class callable_obj
{
public:
// 对象可以有类似函数的调用
void operator ()(int a)
{
std::cout << "This object is callable: " << a << std::endl;
}
};
int main()
{
// 可调用的函数初始化线程
std::thread t1(func, 1);
// // t1.get_id(): 很大的数 t1.joinable(): true
std::cout << "t1 thread_id: " << t1.get_id() << " joinable: " << t1.joinable() << std::endl;
// 可调用的对象初始化线程
std::thread t2;
// t2.get_id(): 0 t2.joinable(): false
std::cout << "t2 thread_id: " << t2.get_id() << " joinable: " << t2.joinable() << std::endl;
callable_obj obj;
// 移动赋值, 也可以用t1的直接构造
t2 = std::move(std::thread(obj, 2));
// t2.get_id(): 很大的数 t2.joinable(): true
std::cout << "t2 thread_id: " << t2.get_id() << " joinable: " << t2.joinable() << std::endl;
return 0;
}
t1 thread_id: 140549800912640 joinable: This function is callable: 11
t2 thread_id: thread::id of a non-executing thread joinable: 0
t2 thread_id: 140549792519936 joinable: 1
terminate called without an active exception
This object is callable: 2
需要主要的我们并没有显示执行t1和t2线程,但是线程还是执行了,但是会发生异常。
#include
[public]
void std::thread::join();
// 仅当joinable为true才能调用本函数, 否则抛出异常system_error。
// 本函数是使主线程进入阻塞状态,等待子线程执行完成后,主线程在继续执行,joinable函数返回false。
// 当子线程执行结束后,资源由主线程回收,主线程继续执行后续指令。
void std::thread::detach();
// 仅当joinable为true才能调用本函数, 否则抛出异常system_error。
// 本函数调用后主线程不会去等待子线程是否执行完成,主线程继续执行后续指令,joinable函数返回false。
// 可能会发生主线程已经执行结束,子线程还未运行结束的情况。
// 当线程结束时,分离的线程由系统回收。
#include
#include
void func(int a)
{
std::cout << "This function is callable: " << a << std::endl;
}
class callable_obj
{
public:
void operator ()(int a)
{
std::cout << "This object is callable: " << a << std::endl;
}
};
int main()
{
std::thread t1(func, 1);
// 分离,后面的代码继续处理
t1.detach();
callable_obj obj;
std::thread t2(std::thread(obj, 2));
// 线程进入阻塞, t2线程执行完成才会执行return 0
t2.join();
return 0;
}
This function is callable: 1
This object is callable: 2
本实例并没有什么不对, 都会输出,因为主线程处理后续代码的时间较长, t1大概率会在主线程执行完执行结束。因此,不会发生什么特殊的情况,但是很多时候分离需要考虑到这个问题。
传参问题还需要多注意下,会出现一些问题。在参数是值的情况正常不会发生,但是如果参数有引用就会发生错误,需要使用std::ref来告诉其传递的是引用 。
#include
#include
#include
void func(int a, int& b)
{
std::cout << "func: a = " << a << ", b = " << b << std::endl;
b = 7;
}
int main()
{
int a = 2, b = 2;
// std::thread t(func, a, b) 错误
std::thread t(func, a, std::ref(b));
t.join();
std::cout << "main: a = " << a << ", b = " << b << std::endl;
return 0;
}
func: a = 2, b = 2
main: a = 2, b = 7
#include
#include
#include
void func(int a, int& b)
{
std::cout << "func: a = " << a << ", b = " << b << std::endl;
b = 7;
}
class thread_graud
{
std::thread _t;
public:
thread_graud(std::thread& t) : _t(std::move(t)) {}
~thread_graud()
{
if(_t.joinable()) _t.join();
}
thread_graud(const thread_graud&) = delete;
thread_graud& operator =(const thread_graud&) = delete;
};
int main()
{
int a = 2, b = 2;
std::thread t(func, a, std::ref(b));
thread_graud g(t);
std::cout << "main: a = " << a << ", b = " << b << std::endl;
return 0;
}
使用中间类thread_graud来隐式调用join方法。
c++11的线程是可以使用平台的特性扩展的,没想到吧。
#include
std::native_handle_type std::native_handle();
// 本函数会返回对于平台下底层句柄, 其条件也是joinable为true, 本人的系统是linux,此函数就会返回pthread_t
#include
#include
#include
void func(int a)
{
std::cout << "linux 系统调用成功 a = " << a << std::endl;
}
int main()
{
int a = 2;
std::thread t(func, a);
// 使用linux平台下的pthread_join来调用线程
pthread_join(t.native_handle(), nullptr);
return 0;
}
linux 系统调用成功 a = 2
terminate called without an active exception
虽然可以这样来用, 但是由于使用linux下的线程函数启动了线程,而joinable的值并没有发生改变,线程对象析构时还是会调用std::terminate(),导致了异常的发生。