std::thread是 C++11 标准库中用于多线程编程的核心类,提供线程的创建、管理和同步功能。下面我们一一讲解。
一.构造函数
官网的构造函数如下:
1.默认构造函数和线程创建
thread() noexcept;
作用:创建一个 std::thread
对象,但不关联任何实际线程(即空线程对象)。
如:
std::thread t; // 空线程对象,不执行任何操作
注意:空线程对象不可调用 join()
或 detach()
,需先绑定有效线程。
2.初始化构造函数
template <class Fn, class... Args>
explicit thread(Fn&& fn, Args&&... args);
- 作用:创建一个新线程,新线程会立即执行
fn(args...)函数
。 - 参数:
fn
:可调用对象(函数、Lambda、函数对象等)。args
:传递给fn
的参数
如:
void print(int x) { std::cout << x; }
std::thread t(print, 42); // 线程执行 print(42)
t.join();
这里要注意下面几点
explicit
禁止隐式类型转换(如避免误将函数指针转线程对象)。- 参数默认按值拷贝传递,若需传引用需用
std::ref
- 示例:
int x = 42;
std::thread t([](int& v) { v++; }, std::ref(x)); // 传递引用
3.拷贝构造函数(被删除)
thread(const thread&) = delete;
- 作用:禁止拷贝构造线程对象(线程资源不可复制)。
- 示例:
-
std::thread t1([]{ /* ... */ }); std::thread t2 = t1; // 编译错误!拷贝构造被禁用
- 原因:线程是独占资源,拷贝可能导致线程重复管理(如多次
join()
)。
4.移动构造函数
thread(thread&& x) noexcept;
- 作用:将线程所有权从
x
转移到新对象(x
变为空线程对象)。 - 示例:
std::thread t1([]{ /* ... */ }); std::thread t2 = std::move(t1); // t1 变为空,t2 接管线程 t2.join();
这里要注意下面几点:
- 移动后,原线程对象
x
不再关联任何线程。- 用于将线程对象存入容器或转移所有权:std::vector<std::thread> threads; threads.push_back(std::thread([]{ /* ... */ })); // 必须用移动语义
std::vector<std::thread> threads;
threads.push_back(std::thread([]{ /* ... */ })); // 必须用移动语义
二.赋值运算符重载
1. 移动赋值运算符(Move Assignment)
thread& operator=(thread&& rhs) noexcept;
- 关键行为:
- 当前对象会先释放自己原本持有的线程资源(如果存在)。
- 接管
rhs
的线程所有权,rhs
变为空线程对象(不再关联任何线程)。 - 操作是原子的,且标记为
noexcept
(保证不抛出异常)。
- 示例:
std::thread t1([]{ /* 任务 1 */ }); std::thread t2; t2 = std::move(t1); // t1 的线程所有权转移给 t2,t1 变为空 t2.join();
- 典型用途:
- 将线程对象存入容器(如
std::vector<std::thread>
)。 - 动态管理线程所有权(如线程池)。
- 将线程对象存入容器(如
2. 拷贝赋值运算符(被删除)
thread& operator=(const thread&) = delete;
- 作用:禁止拷贝赋值(编译时报错)。
- 原因:
- 线程是独占资源,拷贝会导致多个对象管理同一线程,引发重复
join()
或detach()
。 - 保证线程对象的唯一所有权,避免资源管理冲突。
- 线程是独占资源,拷贝会导致多个对象管理同一线程,引发重复
- 错误示例:
std::thread t1([]{ /* ... */ }); std::thread t2; t2 = t1; // 编译错误!拷贝赋值被禁用
三.线程等待和线程分离
线程等待就是让主线程等待子线程执行完毕,首先我们要明白为什么要进行线程等待。
1.主线程是进程的入口,若主线程执行完毕,操作系统会直接终止整个进程(包括所有子线程),无论子线程是否完成任务。
2.子线程可能持有共享资源(如内存、文件句柄、网络连接),若主线程不等待子线程结束就退出,可能导致:资源泄漏(如未关闭的数据库连接)和数据竞争(子线程访问已被主线程释放的内存,导致崩溃)。
3.部分情况下线程执行计算或数据处理后,主线程需要汇总结果
线程分离就是用于解除线程与其创建者(如主线程)的关联,使得子线程在终止后能够自动释放资源,无需其他线程显式调用 join()
等待或回收。
下面我们就来讲解一下线程等待和线程分离函数join()
和 detach()
join()
:在主线程中调用阻塞主线程,直到当前线程执行完毕,就是让主线程等待子线程执行完毕
detach()
:分离线程,使其在后台独立运行(无法再管理)。
必须在 std::thread
对象销毁前调用 join()
或 detach()
,否则程序终止。
std::thread t([]{ /* ... */ });
if (t.joinable()) {
t.join(); // 或 t.detach();
}
四.线程 ID 和命名
-
get_id()
:获取线程唯一标识符。 -
std::this_thread
命名空间提供当前线程操作。有get_id(),yield(),sleep_for()等操作。
std::thread t([]{
std::cout << "Thread ID: "
<< std::this_thread::get_id() << "\n";
});
std::cout << "Main thread ID: " << t.get_id() << "\n";
t.join();
五.线程中的同步机制
1. 互斥量 std::mutex
防止多个线程同时访问共享资源。
std::mutex mtx;
int counter = 0;
void safe_increment() {
std::lock_guard<std::mutex> lock(mtx); // 自动加锁解锁
counter++;
}
std::thread t1(safe_increment);
std::thread t2(safe_increment);
t1.join(); t2.join();
2. 条件变量 std::condition_variable
用于线程间的条件同步。
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
void wait_thread() {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return ready; }); // 等待条件满足
std::cout << "Ready!\n";
}
void signal_thread() {
std::this_thread::sleep_for(std::chrono::seconds(1));
{
std::lock_guard<std::mutex> lock(mtx);
ready = true;
}
cv.notify_one(); // 通知等待线程
}
std::thread t1(wait_thread);
std::thread t2(signal_thread);
t1.join(); t2.join();
六.实践操作:
用两个线程分别交替打印 1-100 的奇数和偶数,通过互斥锁和条件变量实现交替输出:
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
using namespace std;
int num = 1; // 共享计数器
mutex mtx; // 互斥锁
condition_variable cv; // 条件变量
const int MAX_NUM = 100; // 最大值
void print_odd() {
while (true) {
unique_lock<mutex> lock(mtx);
// 等待条件:数字为奇数或超过最大值
cv.wait(lock, [] {
return (num % 2 == 1) || (num > MAX_NUM);
});
if (num > MAX_NUM) break;
cout << "Odd: " << num << endl;
num++; // 递增到偶数
lock.unlock();
cv.notify_all(); // 唤醒另一个线程
}
}
void print_even() {
while (true) {
unique_lock<mutex> lock(mtx);
// 等待条件:数字为偶数或超过最大值
cv.wait(lock, [] {
return (num % 2 == 0) || (num > MAX_NUM);
});
if (num > MAX_NUM) break;
cout << "Even: " << num << endl;
num++; // 递增到奇数
lock.unlock();
cv.notify_all(); // 唤醒另一个线程
}
}
int main() {
thread t1(print_odd);
thread t2(print_even);
t1.join();
t2.join();
return 0;
}
运行结果
c++的线程就讲到这里,有帮助的话就点点赞吧。