std::mutex是C++多线程同步的核心工具,用于保护共享资源避免竞态条件。通过lock()和unlock()手动加锁或使用RAII风格的std::lock_guard、std::unique_lock可确保资源访问的互斥性。竞态条件源于线程执行顺序的不确定性,导致数据不一致,如未加锁的共享计数器自增出错。C++11后提供多种同步机制:std::atomic适用于简单原子操作;std::shared_mutex适合读多写少场景;std::condition_variable支持线程间等待通知;std::unique_lock提供更灵活的锁管理。避免死锁的关键是统一加锁顺序或使用std::scoped_lock原子化获取多锁。提升性能需细化锁粒度、减少锁竞争、使用无锁结构或std::call_once优化初始化。正确选择同步工具需权衡场景复杂度与性能需求,优先从简单机制入手。

C++并发编程中,线程安全是一个绕不开的核心议题,而
std::mutex
std::mutex
std::mutex
要实现C++中的线程安全,并妥善使用
std::mutex
std::mutex
lock()
unlock()
#include <iostream>
#include <vector>
#include <string>
#include <thread>
#include <mutex> // 包含mutex头文件
std::vector<int> shared_data;
std::mutex mtx; // 定义一个全局或成员互斥量
void add_to_shared_data(int value) {
mtx.lock(); // 锁定互斥量
// 保护对shared_data的访问
shared_data.push_back(value);
std::cout << "Thread " << std::this_thread::get_id() << " added: " << value << std::endl;
mtx.unlock(); // 解锁互斥量
}
// 实际开发中,更推荐使用RAII风格的锁,如std::lock_guard或std::unique_lock
void add_to_shared_data_raii(int value) {
std::lock_guard<std::mutex> lock(mtx); // 构造时锁定,析构时自动解锁
shared_data.push_back(value);
std::cout << "Thread " << std::this_thread::get_id() << " added (RAII): " << value << std::endl;
// lock_guard对象在函数结束时(或离开其作用域时)会自动析构并解锁mtx
}
int main() {
std::vector<std::thread> threads;
for (int i = 0; i < 5; ++i) {
threads.emplace_back(add_to_shared_data_raii, i * 10);
}
for (auto& t : threads) {
t.join();
}
std::cout << "Final shared_data size: " << shared_data.size() << std::endl;
return 0;
}在上面的例子中,
add_to_shared_data_raii
std::lock_guard
std::lock_guard
这确实是我在初学并发时最困惑的地方。很多时候,我们写了一个多线程程序,在测试环境下跑了几次,结果似乎都是正确的,但一旦部署到生产环境,或者在负载较高时,各种奇怪的错误就冒出来了。这背后隐藏的元凶,就是“竞态条件”(Race Condition)。
立即学习“C++免费学习笔记(深入)”;
竞态条件指的是,多个线程以某种不确定的顺序访问和修改共享数据,最终结果取决于这些线程执行的精确时序,而这种时序是不可预测的。最经典的例子就是对一个共享计数器进行增量操作。假设我们有一个
int counter = 0;
counter++;
counter
如果两个线程的执行时序是这样的:
counter
counter
counter
counter
counter
counter
最终
counter
C++11及后续标准在并发编程方面确实带来了革命性的进步,不仅仅是
std::mutex
std::unique_lock
std::lock_guard
std::unique_lock
std::defer_lock
try_lock()
try_lock_for()
try_lock_until()
std::unique_lock
std::recursive_mutex
std::shared_mutex
std::shared_timed_mutex
std::shared_mutex
std::shared_lock<std::shared_mutex>
std::unique_lock<std::shared_mutex>
std::condition_variable
wait()
notify_one()
notify_all()
std::unique_lock
std::mutex
std::atomic
std::atomic
std::atomic
选择哪种机制,取决于你的具体需求:
std::lock_guard
std::mutex
std::unique_lock
std::mutex
std::shared_mutex
std::condition_variable
std::atomic
通常,我倾向于从最简单的
std::lock_guard
std::mutex
即便
std::mutex
常见陷阱:
死锁(Deadlock):这是并发编程中最臭名昭著的问题之一。当两个或多个线程各自持有一个锁,并尝试获取对方持有的锁时,就会发生死锁。
std::mutex m1, m2;
void func1() {
m1.lock();
std::this_thread::sleep_for(std::chrono::milliseconds(10)); // 模拟工作
m2.lock(); // 尝试获取m2,但可能被func2持有
// ...
m2.unlock();
m1.unlock();
}
void func2() {
m2.lock();
std::this_thread::sleep_for(std::chrono::milliseconds(10)); // 模拟工作
m1.lock(); // 尝试获取m1,但可能被func1持有
// ...
m1.unlock();
m2.unlock();
}当
func1
m1
m2
func2
m2
m1
忘记解锁:如果手动使用
lock()
unlock()
lock()
unlock()
unlock()
std::lock_guard
std::unique_lock
锁的粒度问题:如果锁定的范围过大(粗粒度锁),会导致并发度降低,因为大量不相关的操作都被串行化了。如果锁定的范围过小(细粒度锁),虽然提高了并发度,但管理多个锁的复杂性会增加,也更容易引入死锁。
避免死锁的技巧:
统一加锁顺序:这是最简单也最有效的策略。确保所有线程在需要获取多个锁时,都以相同的顺序获取这些锁。在上面的死锁示例中,如果
func2
m1
m2
使用std::lock()
std::scoped_lock
std::lock(mtx1, mtx2, ...)
std::scoped_lock
std::lock_guard
std::lock
// 使用std::scoped_lock (C++17)
void safe_func1() {
std::scoped_lock lock(m1, m2); // 原子性锁定m1和m2
// ...
}
// 使用std::lock (C++11)
void safe_func2() {
std::unique_lock<std::mutex> lk1(m1, std::defer_lock); // 延迟锁定
std::unique_lock<std::mutex> lk2(m2, std::defer_lock);
std::lock(lk1, lk2); // 原子性锁定
// ...
}提升并发性能的技巧:
精细化锁的粒度:只锁定真正需要保护的共享数据,尽可能缩短锁定的时间。如果一个函数大部分操作都是本地计算,只有一小部分涉及到共享资源,那么只对那部分共享资源操作加锁。
避免不必要的锁定:有些数据在多线程环境中是只读的,或者每个线程都有自己的副本,这些情况下就不需要加锁。
使用无锁数据结构或原子操作:对于一些特定的场景,比如计数器、队列等,可以考虑使用C++标准库提供的
std::atomic
std::queue
利用std::call_once
std::call_once
std::once_flag
减少锁竞争:如果多个线程频繁地竞争同一个锁,会严重影响性能。考虑重新设计数据结构,将共享数据拆分成更小的、独立的单元,每个单元有自己的锁,从而减少锁的竞争。例如,使用多个哈希表,每个哈希表有自己的锁,而不是一个巨大的哈希表用一个全局锁。
总之,
std::mutex
以上就是C++线程安全与std::mutex使用方法的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号