尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

互斥锁管理之lock_guard和scope_lock学习

互斥锁管理之lock_guard和scope_lock学习 前面博客在多线程中处理访问共享资源使用mutex的lock和unlock来对数据进行保护避免数据据竞争。如果对某一段功能代码进行保护每次都要lock和unlock如果忘记unlock了那么多线程访问就卡着永远不会结束了本篇记录一种简便的方法来进行数据保护即lock_guard和scope_lock。先看之前的mutex的示例//lock_guard.cpp lock_guard example //#include iostream // std::cout #include thread // std::thread #include mutex // std::mutex //临界区互斥锁 std::mutex mtx; // mutex for critical section int g_count 0; //自增函数测试 void incrementation() { printf(incrementationstart lock\n); mtx.lock(); for (int i 0; i 1000000; i) { g_count; } //模拟耗时 std::this_thread::sleep_for(std::chrono::seconds(1)); //休眠1秒 printf(incrementationend lock\n); mtx.unlock(); } int main() { std::thread t1(incrementation); std::thread t2(incrementation); t1.join(); t2.join(); //理论应该 2000000不加锁实际永远小于这个数 printf(g_count%d\n, g_count); printf(hello learn mutex\n); return 0; }编译运行如果忘记unlock()了那这个程序就永远不会结束了。1.lock_guard现在把它改成std::lock_guardstd::mutex lock(mtx);一行代码就解决这种问题。//lock_guard.cpp lock_guard example //#include iostream // std::cout #include thread // std::thread #include mutex // std::mutex //临界区互斥锁 std::mutex mtx; // mutex for critical section int g_count 0; //自增函数测试 void incrementation() { printf(incrementationstart lock\n); std::lock_guardstd::mutex lock(mtx); for (int i 0; i 1000000; i) { g_count; } //模拟耗时 std::this_thread::sleep_for(std::chrono::seconds(1)); //休眠1秒 printf(incrementationend lock\n); } int main() { std::thread t1(incrementation); std::thread t2(incrementation); t1.join(); t2.join(); //理论应该 2000000不加锁实际永远小于这个数 printf(g_count%d\n, g_count); printf(hello learn lock_guard\n); return 0; }编译运行运行结果是一样的。lock_guard 是互斥体包装器为在作用域块期间占有互斥体提供便利的 RAII 风格机制。当创建lock_guard 对象时它尝试接收给定互斥体的所有权。当控制离开创建 lock_guard 对象的作用域时销毁 lock_guard 并释放互斥体。2.scope_lock上面介绍的是只有一个互斥体如果有两个互斥体呢lock_guard要写两次例如一个银行转帐系统转帐时既要锁住转出账户也要锁入转入账户不然同时操作就乱了。//scoped_lock.cpp scoped_lock example #include iostream #include thread #include mutex #include vector #include chrono #include memory // 如果需要智能指针 class BankAccount { private: int balance_; mutable std::mutex mtx_; // 加 mutable允许在 const 函数中修改 public: explicit BankAccount(int initial) : balance_(initial) {} void transfer(BankAccount target, int amount) { // 错误示例不要这样写可能死锁 // std::lock_guardstd::mutex lock1(mtx_); // 假设先锁 from // std::lock_guardstd::mutex lock2(target.mtx_); // 再锁 target // 如果另一个线程先锁 target 再锁 from就会死锁 // 正确的 lock_guard 写法需要配合 std::lock std::lock(mtx_, target.mtx_); // 先统一锁定 std::lock_guardstd::mutex lock1(mtx_, std::adopt_lock); // 接管已锁 std::lock_guardstd::mutex lock2(target.mtx_, std::adopt_lock); if (balance_ amount) { balance_ - amount; target.balance_ amount; std::cout [成功] 转账 amount 当前账户余额: balance_ std::endl; } else { std::cout [失败] 余额不足当前余额: balance_ std::endl; } } int getBalance() const { std::lock_guardstd::mutex lock(mtx_); return balance_; } }; int main() { BankAccount accountA(1000); BankAccount accountB(500); std::vectorstd::thread threads; // 线程1A - B 转账 200执行3次 threads.emplace_back([]() { for (int i 0; i 3; i) { accountA.transfer(accountB, 200); std::this_thread::sleep_for(std::chrono::milliseconds(10)); } }); // 线程2B - A 转账 100执行3次 threads.emplace_back([]() { for (int i 0; i 3; i) { accountB.transfer(accountA, 100); std::this_thread::sleep_for(std::chrono::milliseconds(10)); } }); // 线程3A - B 转账 300执行2次 threads.emplace_back([]() { for (int i 0; i 2; i) { accountA.transfer(accountB, 300); std::this_thread::sleep_for(std::chrono::milliseconds(15)); } }); for (auto t : threads) { t.join(); } std::cout \n 最终余额 std::endl; std::cout 账户A: accountA.getBalance() std::endl; std::cout 账户B: accountB.getBalance() std::endl; printf(hello learn lock_guard\n); return 0; }编译运行如果是更多个数据项要保护那么又得加多个互斥体这样既窗容易出错也很难维护那边有没有更好的方法呢答案就是用scoped_lock解决这个问题可以同时多个互斥体这是c17的标准。//scoped_lock.cpp scoped_lock example #include iostream #include thread #include mutex #include vector #include chrono #include memory // 如果需要智能指针 class BankAccount { private: int balance_; mutable std::mutex mtx_; // 加 mutable允许在 const 函数中修改 public: explicit BankAccount(int initial) : balance_(initial) {} void transfer(BankAccount target, int amount) { // 使用 std::scoped_lock 同时锁定两个互斥量 std::scoped_lock lock(mtx_, target.mtx_); if (balance_ amount) { balance_ - amount; target.balance_ amount; std::cout [成功] 转账 amount 当前账户余额: balance_ std::endl; } else { std::cout [失败] 余额不足当前余额: balance_ std::endl; } } int getBalance() const { std::lock_guardstd::mutex lock(mtx_); return balance_; } }; int main() { BankAccount accountA(1000); BankAccount accountB(500); std::vectorstd::thread threads; // 线程1A - B 转账 200执行3次 threads.emplace_back([]() { for (int i 0; i 3; i) { accountA.transfer(accountB, 200); std::this_thread::sleep_for(std::chrono::milliseconds(10)); } }); // 线程2B - A 转账 100执行3次 threads.emplace_back([]() { for (int i 0; i 3; i) { accountB.transfer(accountA, 100); std::this_thread::sleep_for(std::chrono::milliseconds(10)); } }); // 线程3A - B 转账 300执行2次 threads.emplace_back([]() { for (int i 0; i 2; i) { accountA.transfer(accountB, 300); std::this_thread::sleep_for(std::chrono::milliseconds(15)); } }); for (auto t : threads) { t.join(); } std::cout \n 最终余额 std::endl; std::cout 账户A: accountA.getBalance() std::endl; std::cout 账户B: accountB.getBalance() std::endl; printf(hello learn lock_guard\n); return 0; }编译运行参考https://cplusplus.com/reference/mutex/lock_guard/https://zh.cppreference.com/cpp/thread/lock_guardhttps://zh.cppreference.com/cpp/thread/scoped_lock
返回列表