栈上竞态条件(Race Condition):如何用互斥锁避免栈对象成员变量竞争?
解决栈对象版本Hello类的竞态条件问题
嘿,这个问题很典型!咱们先拆解一下:你的栈对象版本里,多个线程同时访问并修改Hello类的成员变量_i,没有任何同步机制,这就导致了竞态条件——多个线程的读写操作互相干扰,最终_i的值会出现不可预期的错误。
要解决这个问题,咱们只需要给访问_i的临界区加互斥锁就行,具体步骤如下:
1. 给Hello类添加互斥锁成员
把std::mutex作为Hello类的私有成员,这样每个Hello对象都有自己独立的锁,不会和其他对象的锁冲突,保证锁的粒度合理。
2. 在成员函数中使用锁保护临界区
在say_hello函数里,用std::lock_guard<std::mutex>(或者std::unique_lock)来包裹所有访问和修改_i的代码。std::lock_guard会在构造时自动加锁,析构时自动解锁,避免手动解锁的遗漏。
修改后的完整代码
#include <thread> #include <iostream> #include <vector> #include <mutex> class Hello { int _i; std::mutex _mu; // 给每个Hello对象分配独立的互斥锁 public: Hello() : _i(0) {} // 初始化_i为0 void say_hello() { // 使用lock_guard自动管理锁的生命周期 std::lock_guard<std::mutex> lock(_mu); _i++; // 临界区:修改_i std::cout << "Hello from thread! _i = " << _i << std::endl; // 临界区:读取_i } }; void hellos_in_stack() { Hello h; // 栈对象 std::vector<std::thread> threads; for (int i = 0; i < 5; ++i) { threads.emplace_back(&Hello::say_hello, &h); // 多个线程调用同一个栈对象的成员函数 } for (auto& t : threads) { t.join(); } } void hellos_in_heap() { Hello* h = new Hello(); // 堆对象 std::vector<std::thread> threads; for (int i = 0; i < 5; ++i) { threads.emplace_back(&Hello::say_hello, h); } for (auto& t : threads) { t.join(); } delete h; } int main() { std::cout << "Testing stack object version:\n"; hellos_in_stack(); std::cout << "\nTesting heap object version:\n"; hellos_in_heap(); return 0; }
关键说明
- 锁的封装:把
_mu作为类的成员,确保只有Hello类的成员函数能访问这个锁,符合封装原则,也避免了全局锁带来的不必要的竞争。 - 自动锁管理:
std::lock_guard是RAII风格的锁管理工具,即使say_hello函数中途抛出异常,它也会在析构时自动解锁,不会导致死锁。 - 临界区范围:尽量把锁的范围缩小到必要的代码块(也就是真正访问共享变量
_i的部分),这样能减少锁的持有时间,提升并发效率。
这样修改后,不管是栈对象还是堆对象版本,_i的读写操作都会被互斥锁保护,不会再出现竞态条件啦!
内容的提问来源于stack exchange,提问作者Sumit Jha
相关产品推荐
相关产品推荐

