C++ 第三阶段 并发与异步 - 第一节:std::thread, std::mutex, std::lock_guard
本文系统介绍了C++11多线程编程的核心组件。主要内容包括:1) 多线程基础概念及其优势;2) std::thread的线程创建与管理;3) std::mutex互斥锁的基本用法和潜在问题;4) std::lock_guard的RAII锁管理机制及其异常安全特性。文章还涵盖了常见问题(死锁、资源竞争)的解决方案,并提出最佳实践建议(优先使用RAII锁、优化锁粒度)。通过多个代码示例展示了线程安全编
·
目录
一、多线程编程基础
1. 多线程的意义
- 性能提升:利用多核 CPU 并行执行任务(如计算密集型任务)。
- 响应性增强:在 GUI 程序中保持界面流畅(如后台计算)。
- 复杂场景支持:模拟并发行为(如服务器处理多客户端请求)。
2. C++11 多线程支持
- 核心组件:
std::thread:创建和管理线程。std::mutex:互斥锁,防止多线程竞争共享资源。std::lock_guard:RAII 风格的锁管理工具,确保锁的自动释放。
二、std::thread:线程管理
1. 基本用法
(1) 创建线程
#include <iostream>
#include <thread>
void threadFunction() {
std::cout << "Hello from thread!" << std::endl;
}
int main() {
std::thread t(threadFunction); // 创建线程
t.join(); // 等待线程结束
return 0;
}
(2) 传递参数
void printMessage(const std::string& msg) {
std::cout << msg << std::endl;
}
int main() {
std::thread t(printMessage, "Hello from thread with arguments!");
t.join();
return 0;
}
(3) 线程分离
std::thread t([]() {
std::cout << "Detached thread running..." << std::endl;
});
t.detach(); // 线程独立运行,主线程无需等待
2. 注意事项
- 线程安全:避免多个线程同时修改共享资源(需配合锁使用)。
- 资源管理:确保线程结束后释放资源(通过
join()或detach())。 - 异常处理:线程函数中的异常需在内部捕获,否则可能导致程序崩溃。
三、std::mutex:互斥锁
1. 基本用法
(1) 手动加锁/解锁
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx;
int sharedResource = 0;
void increment() {
mtx.lock(); // 手动加锁
sharedResource++;
std::cout << "Value: " << sharedResource << std::endl;
mtx.unlock(); // 手动解锁
}
int main() {
std::thread t1(increment);
std::thread t2(increment);
t1.join();
t2.join();
return 0;
}
(2) 潜在问题
- 忘记解锁:可能导致死锁(其他线程无法获取锁)。
- 异常导致未解锁:如果
increment()抛出异常,unlock()未被调用。
四、std::lock_guard:RAII 风格锁管理
1. 核心优势
- 自动解锁:构造时加锁,析构时解锁(无论是否抛出异常)。
- 异常安全:避免因异常导致的死锁。
- 简化代码:无需手动调用
lock()/unlock()。
2. 基本用法
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx;
int sharedResource = 0;
void increment() {
std::lock_guard<std::mutex> lock(mtx); // 自动加锁
sharedResource++;
std::cout << "Value: " << sharedResource << std::endl;
// 离开作用域时自动解锁
}
int main() {
std::thread t1(increment);
std::thread t2(increment);
t1.join();
t2.join();
return 0;
}
3. 示例:收紧锁的作用域
void printBlock(int n, char c) {
{
std::lock_guard<std::mutex> lock(mtx); // 仅保护输出代码
for (int i = 0; i < n; ++i) {
std::cout << c;
}
std::cout << std::endl;
} // 离开作用域时自动解锁
// 其他非临界区代码
}
五、常见问题与解决方案
1. 死锁
(1) 原因
- 多个线程相互等待对方持有的锁。
- 锁的顺序不一致。
(2) 解决方案
- 统一加锁顺序:所有线程按固定顺序申请锁。
- 使用
std::lock:同时加锁多个互斥量,避免部分加锁失败。std::mutex m1, m2; void safeLock() { std::lock(m1, m2); // 同时加锁(C++14+) std::lock_guard<std::mutex> lock1(m1, std::adopt_lock); std::lock_guard<std::mutex> lock2(m2, std::adopt_lock); }
2. 资源竞争
(1) 表现
- 多个线程同时修改共享资源,导致数据不一致。
(2) 解决方案
- 使用互斥锁保护共享资源。
- 最小化锁的持有时间:仅保护关键代码段。
3. 锁的性能问题
(1) 锁粒度过大
- 长时间持有锁会阻塞其他线程。
(2) 优化建议
- 细粒度锁:为不同资源使用独立锁。
- 读写锁:使用
std::shared_mutex区分读/写操作(C++17)。
六、最佳实践
1. 优先使用 std::lock_guard
- 替代手动
lock()/unlock():确保锁的自动释放。 - 避免死锁风险:即使发生异常,锁也会被正确释放。
2. 避免锁的滥用
- 仅保护共享资源:非共享数据无需加锁。
- 减少锁的持有时间:快速执行临界区代码。
3. 异常安全
- 在锁的作用域内捕获异常:确保资源一致性。
- 避免在锁保护范围内执行耗时操作:防止死锁和性能下降。
七、总结
1. std::thread
- 创建和管理线程的核心工具。
- 需配合锁机制(如
std::mutex)实现线程安全。
2. std::mutex
- 保护共享资源的基础工具。
- 手动加锁/解锁需谨慎,易导致死锁或资源泄漏。
3. std::lock_guard
- RAII 模式的核心应用,自动管理锁的生命周期。
- 推荐作为首选锁管理工具,确保异常安全和代码简洁性。
4. 后续学习方向
- 高级同步机制:如
std::condition_variable、std::atomic。 - 异步编程:使用
std::async和std::future实现任务调度。
八、示例代码汇总
示例 1:基础线程与锁
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx;
int counter = 0;
void incrementCounter() {
for (int i = 0; i < 1000; ++i) {
std::lock_guard<std::mutex> lock(mtx);
++counter;
}
}
int main() {
std::thread t1(incrementCounter);
std::thread t2(incrementCounter);
t1.join();
t2.join();
std::cout << "Final counter: " << counter << std::endl;
return 0;
}
示例 2:收紧锁的作用域
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx;
int sharedData = 0;
void processData() {
// 非临界区代码
{
std::lock_guard<std::mutex> lock(mtx); // 保护关键代码
sharedData += 1;
std::cout << "Processed data: " << sharedData << std::endl;
} // 离开作用域时自动解锁
// 其他非临界区代码
}
int main() {
std::thread t1(processData);
std::thread t2(processData);
t1.join();
t2.join();
return 0;
}
示例 3:避免死锁(统一加锁顺序)
#include <iostream>
#include <thread>
#include <mutex>
std::mutex m1, m2;
void safeThread() {
std::lock(m1, m2); // 同时加锁(C++14+)
std::lock_guard<std::mutex> lock1(m1, std::adopt_lock);
std::lock_guard<std::mutex> lock2(m2, std::adopt_lock);
std::cout << "Safe lock acquired" << std::endl;
}
int main() {
std::thread t1(safeThread);
std::thread t2(safeThread);
t1.join();
t2.join();
return 0;
}
通过掌握 std::thread、std::mutex 和 std::lock_guard,开发者可以构建线程安全、高效并发的 C++ 程序,为更复杂的多线程场景(如异步任务、条件变量)打下坚实基础。
更多推荐


所有评论(0)