在C++17和C++20标准中,多线程功能主要通过<thread>、<mutex>、<atomic>、<future>、<shared_mutex>等头文件提供的类和函数来实现。这些标准在C++11的基础上进行了扩展和优化,增强了多线程编程的能力。
以下是C++17和C++20中实现多线程的核心类及其原理、使用示例和注意事项:
一、核心多线程类及其实现原理
1. std::thread(线程类)
实现原理:
std::thread是对操作系统原生线程(如POSIX pthread或Windows线程)的封装。- 在构造时,会调用系统API创建一个新的执行流(线程),并执行传入的可调用对象(函数、lambda、函数对象等)。
- 每个
std::thread对象管理一个独立的线程,支持移动语义(move semantics),但不支持拷贝。
C++17/C++20 新特性:
- C++17引入了
std::jthread(在C++20中正式加入标准),支持自动join和协作式中断。
2. std::jthread(C++20新增)
实现原理:
std::jthread是“joining thread”的缩写,是std::thread的增强版本。- 它在析构时会自动调用
join(),避免了资源泄漏。 - 支持协作式中断(cooperative interruption),通过
std::stop_token和std::stop_source机制通知线程停止。
3. std::mutex、std::timed_mutex、std::recursive_mutex
实现原理:
std::mutex是互斥锁,基于操作系统提供的互斥原语(如futex、CriticalSection)实现。- 保证同一时间只有一个线程可以进入临界区。
std::timed_mutex支持带超时的锁获取(try_lock_for、try_lock_until)。std::recursive_mutex允许同一线程多次加锁,避免死锁。
4. std::shared_mutex(C++17引入)
实现原理:
- 支持共享/独占两种锁模式。
- 多个读线程可以同时持有共享锁(
shared_lock),但写线程必须独占(unique_lock)。 - 基于读写锁(reader-writer lock)机制实现,适用于读多写少的场景。
5. std::atomic<T>(原子操作)
实现原理:
- 提供对特定类型
T的原子操作(如load、store、fetch_add等)。 - 底层依赖CPU的原子指令(如x86的
LOCK前缀指令、ARM的LDREX/STREX)。 - 避免使用锁的情况下实现线程安全的数据访问。
6. std::future 和 std::promise
实现原理:
- 用于异步操作的结果传递。
std::promise设置值,std::future获取该值。- 通过共享状态(shared state)在线程间传递数据,支持
get()阻塞等待结果。
7. std::condition_variable
实现原理:
- 与
std::mutex配合使用,实现线程间的等待/通知机制。 - 线程可以等待某个条件成立(
wait),其他线程在条件满足时通知(notify_one或notify_all)。
二、使用示例
示例1:std::thread 基本使用
#include <iostream>
#include <thread>
#include <vector>
void worker(int id) {
for (int i = 0; i < 3; ++i) {
std::cout << "Worker " << id << " working...\n";
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
int main() {
std::vector<std::thread> threads;
for (int i = 0; i < 3; ++i) {
threads.emplace_back(worker, i);
}
// 等待所有线程结束
for (auto& t : threads) {
t.join();
}
return 0;
}
注意:必须调用
join()或detach(),否则程序会std::terminate。
示例2:std::jthread(C++20)自动join和中断
#include <iostream>
#include <thread>
#include <chrono>
void worker_with_stop(std::stop_token stoken) {
for (int i = 0; i < 10; ++i) {
if (stoken.stop_requested()) {
std::cout << "Worker stopped.\n";
return;
}
std::cout << "Working... " << i << "\n";
std::this_thread::sleep_for(std::chrono::milliseconds(200));
}
}
int main() {
std::jthread jt(worker_with_stop);
std::this_thread::sleep_for(std::chrono::milliseconds(500));
// 请求停止
jt.request_stop();
// 析构时自动join
return 0;
}
优势:无需手动
join,支持安全中断。
示例3:std::shared_mutex 读写锁
#include <iostream>
#include <thread>
#include <shared_mutex>
#include <vector>
std::shared_mutex rw_mutex;
int data = 0;
void reader(int id) {
for (int i = 0; i < 3; ++i) {
std::shared_lock<std::shared_mutex> lock(rw_mutex);
std::cout << "Reader " << id << " reads data: " << data << "\n";
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
}
void writer(int id) {
for (int i = 0; i < 2; ++i) {
std::unique_lock<std::shared_mutex> lock(rw_mutex);
data += id;
std::cout << "Writer " << id << " writes data: " << data << "\n";
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
int main() {
std::vector<std::thread> threads;
for (int i = 0; i < 3; ++i) {
threads.emplace_back(reader, i);
}
for (int i = 1; i <= 2; ++i) {
threads.emplace_back(writer, i);
}
for (auto& t : threads) {
t.join();
}
return 0;
}
适用场景:读操作频繁,写操作较少。
示例4:std::atomic 原子操作
#include <iostream>
#include <thread>
#include <atomic>
#include <vector>
std::atomic<int> counter(0);
void increment() {
for (int i = 0; i < 1000; ++i) {
counter.fetch_add(1, std::memory_order_relaxed);
}
}
int main() {
std::vector<std::thread> threads;
for (int i = 0; i < 10; ++i) {
threads.emplace_back(increment);
}
for (auto& t : threads) {
t.join();
}
std::cout << "Final counter value: " << counter.load() << "\n";
return 0;
}
注意:原子操作不保证顺序一致性,必要时使用更强的内存序(如
std::memory_order_seq_cst)。
示例5:std::future 和 std::async
#include <iostream>
#include <future>
#include <chrono>
int heavy_computation() {
std::this_thread::sleep_for(std::chrono::seconds(2));
return 42;
}
int main() {
// 异步启动任务
std::future<int> fut = std::async(std::launch::async, heavy_computation);
std::cout << "Doing other work...\n";
int result = fut.get(); // 阻塞等待结果
std::cout << "Result: " << result << "\n";
return 0;
}
注意:
std::async可能不会创建新线程(取决于std::launch::deferred策略)。
三、注意事项
-
资源管理:
std::thread必须在销毁前调用join()或detach(),否则程序终止。- 推荐使用
std::jthread(C++20)避免此问题。
-
死锁预防:
- 避免嵌套锁或锁顺序不一致。
- 使用
std::lock()一次性锁定多个互斥量。
-
性能考虑:
- 频繁加锁影响性能,考虑无锁数据结构或原子操作。
std::shared_mutex适用于读多写少,否则可能不如普通互斥锁。
-
内存序(Memory Order):
- 原子操作的内存序影响性能和正确性,合理选择
memory_order。
- 原子操作的内存序影响性能和正确性,合理选择
-
异常安全:
- 确保锁在异常发生时能正确释放,使用RAII(如
std::lock_guard)。
- 确保锁在异常发生时能正确释放,使用RAII(如
-
线程局部存储:
- 使用
thread_local关键字声明线程局部变量,避免共享。
- 使用
-
避免数据竞争:
- 所有共享数据必须通过同步机制(锁、原子、消息传递)访问。
总结
| 类/机制 | 用途 | C++标准 |
|---|---|---|
std::thread | 创建和管理线程 | C++11 |
std::jthread | 自动join和中断的线程 | C++20 |
std::mutex | 互斥锁 | C++11 |
std::shared_mutex | 读写锁 | C++17 |
std::atomic<T> | 原子操作 | C++11 |
std::future/promise | 异步结果传递 | C++11 |
std::condition_variable | 条件变量 | C++11 |
C++17和C++20在多线程方面增强了易用性和安全性(如std::shared_mutex、std::jthread),开发者应结合具体场景选择合适的同步机制,确保程序的正确性、性能和可维护性。
1533

被折叠的 条评论
为什么被折叠?



