C++11 之后有了标准的线程库:std::thread。
Linux环境下,C++的std::thread
库底层是对pthread的封装。
一旦一个std::thread绑定了一个函数(如通过有参构造函数构造了一个std::thread对象),则此对象就会立刻开始执行传递进来的函数。
C++ std::thread | 菜鸟教程 (runoob.com)
1. 使用std::thread
- 包含头文件: #include <thread>
- 使用CMake编译包含std::thread的文件时,需要显式引入外部依赖包:
find_package(Threads) # 引入外部依赖包
add_executable(ProjectName main.cpp)
target_link_libraries (${PROJECT_NAME} ${CMAKE_THREAD_LIBS_INIT}) # 链接Thread库
# 或者下面这种方式
# target_link_libraries (${PROJECT_NAME} pthread)
# 注:使用target_link_libraries链接库时,需要在add_executable之后
1.1 构造函数
ℳ: std::thread::thread - cppreference.com
thread() noexcept; | (1) | (since C++11) |
thread( thread&& other ) noexcept; | (2) | (since C++11) |
template< class Function, class... Args > | (3) | (since C++11) |
thread( const thread& ) = delete; | (4) | (since C++11) |
- 默认构造函数,创建一个空的
std::thread
执行对象。 - Move 构造函数,move 构造函数(move 语义是 C++11 新出现的概念,详见附录),调用成功之后
x
不代表任何std::thread
执行对象。 - 初始化构造函数,创建一个
std::thread
对象,该std::thread
对象可被joinable
,新产生的线程会调用fn
函数,该函数的参数由args
给出。 - 拷贝构造函数(被禁用),意味着
std::thread
对象不可拷贝构造。
注意:可被
joinable
的std::thread
对象必须在他们销毁之前被主线程join
或者将其设置为detached
.
1.2 构造函数示例
#include <iostream>
#include <utility>
#include <thread>
#include <chrono>
#include <functional>
#include <atomic>
void f1(int n)
{
for (int i = 0; i < 5; ++i) {
std::cout << "Thread " << n << " executing\n";
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
void f2(int& n)
{
for (int i = 0; i < 5; ++i) {
std::cout << "Thread 2 executing\n";
++n;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
int main()
{
int n = 0;
std::thread t1; // t1 is not a thread
std::thread t2(f1, n + 1); // pass by value
std::thread t3(f2, std::ref(n)); // pass by reference
std::thread t4(std::move(t3)); // t4 is now running f2(). t3 is no longer a thread
t2.join();
t4.join();
std::cout << "Final value of n is " << n << '\n';
}
2. join() 和 detach()
2.1 join()
join: join线程。 调用此函数,主线程需要一直等待子线程执行完毕,才可继续往下执行。
调用join() 函数之后:
- joinable() == false
2.2 detach()
detach: detach 线程。 将当前线程对象所代表的执行实例与该线程对象分离,使得线程的执行可以在后台单独进行。 意味着主线程不需要等待子线程执行完毕才能往下继续执行(不阻塞主线程)。
一旦线程执行完毕,它所分配的资源将会被释放。
调用 detach 函数之后:
*this
不再代表任何的线程执行实例。- joinable() == false
- get_id() == std::thread::id()
另外,如果出错或者 joinable() == false,则会抛出 std::system_error。
2.3 joinable() 与 join()和detach()之间的关系
C++的std::thread类型有一个std::thread::joinable()方法,该方法可以判断一个std::thread对象的状态是否为joinable。
当调用detach() 或join() 方法时,thread对象的状态将由 joinable转为 non-joinable,也就是std::thread::joinable()的返回值会由true变为false。
2.4 thread对象不调用join()也不调用detach()会发生什么?
首先来看一下 std::thread的析构函数内容:
~thread()
{
if (joinable())
std::terminate();
}
可以看出如果一个std::thread对象在析构时,其joinable()状态为 true,则会直接调用std::terminate() 去中断程序。
根据图1可知,通过有参构造函数创建了一个 std::thread对象之后,其状态时 joinable() == true的,只有当 调用 .join() 或者 . detach之后, joinable() == false。
所以如果 thread对象创建之后,绑定了执行函数,既不调用join() 也不调用 detac(),则在其对象释放之时,系统会直接 中断⛔️(std::terminate)