美文网首页
C++11_lock_guard的线程死锁问题和解决

C++11_lock_guard的线程死锁问题和解决

作者: JasonLiThirty | 来源:发表于2020-02-29 16:06 被阅读0次

视频教程:https://www.bilibili.com/video/av92453755

std::lock_guard

  • std::lock_guard严格基于作用域(scope-based)的锁管理类模板,构造时是否加锁是可选的(不加锁时假定当前线程已经获得锁的所有权),析构时自动释放锁,所有权不可转移,对象生存期内不允许手动加锁和释放锁。
  • 默认构造函数里锁定互斥量,即调用互斥量的lock函数。
  • 析构函数里解锁互斥量,即调用互斥量的unlock函数。
  • std::lock_guard 对象并不负责管理 Mutex 对象的生命周期,lock_guard 只是简化了 Mutex 对象的上锁和解锁操作,方便线程对互斥量上锁,即在某个 lock_guard对象的声明周期内,它所管理的锁对象会一直保持上锁状态;而 lock_guard的生命周期结束之后,它所管理的锁对象会被解锁

std::unique_lock

  • 与std:::lock_gurad基本一致,但更加灵活的锁管理类模板,构造时是否加锁是可选的,在对象析构时如果持有锁会自动释放锁,所有权可以转移。对象生命期内允许手动加锁和释放锁。但提供了更好的上锁和解锁控制尤其是在程序抛出异常后先前已被上锁的 Mutex 对象可以正确进行解锁操作,极大地简化了程序员编写与 Mutex 相关的异常处理代码****。
  • std::unique_lock的上锁/解锁操作:lock,try_lock,try_lock_for,try_lock_until 和unlock

两者区别

  • std::unique_lock更灵活,提供了lock, unlock, try_lock等接口
  • std::lock_guard更简单,没有多余的接口,构造函数时拿到锁,析构函数时释放锁,但更省时

线程死锁

因为std::lock_guard更简单的特性,所以可能出现下列情况的线程死锁

#include <iostream>
#include <chrono>
#include <mutex>
#include <thread>
std::mutex mt1;
std::mutex mt2;
void deadLock(std::mutex& mtA, std::mutex& mtB)
{

    std::lock_guard<std::mutex>lock1(mtA);
    std::cout << "get the first mutex" << " in thread " << std::this_thread::get_id() << std::endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(1));

    std::lock_guard<std::mutex>lock2(mtB);
    std::cout << "get the second mutex" << " in thread " << std::this_thread::get_id() << std::endl;


    std::cout << "do something in thread " << std::this_thread::get_id() << std::endl;
}


int main() {
    std::thread t1([&] {deadLock(mt1, mt2); });
    std::thread t2([&] {deadLock(mt2, mt1); });
    t1.join();
    t2.join();
}

解决方式:使用锁定策略+原子锁方式来防止死锁情况

方法一:先同时lock掉两个锁,再构建std::lock_guard

构建lock_guard时,Tag 参数为 std::adopt_lock,表明当前线程已经获得了锁,不需要再锁了,此后mt对象的解锁操作交由 lock_guard 对象 guard 来管理,在 guard 的生命周期结束之后,mt对象会自动解锁。

#include <iostream>
#include <chrono>
#include <mutex>
#include <thread>
#include <assert.h>
std::mutex mt1;
std::mutex mt2
void deadLockProcess1(std::mutex& mtA, std::mutex& mtB)
{
    std::lock(mtA, mtB);

    std::lock_guard<std::mutex>lock1(mtA, std::adopt_lock);
    std::cout << "get the first mutex" << " in thread " << std::this_thread::get_id() << std::endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(1));

    std::lock_guard<std::mutex>lock2(mtB, std::adopt_lock);
    std::cout << "get the second mutex" << " in thread " << std::this_thread::get_id() << std::endl;
    std::cout << "do something in thread " << std::this_thread::get_id() << std::endl;
}

int main() {
    std::thread t1([&] {deadLockProcess1(mt1, mt2); });
    std::thread t2([&] {deadLockProcess1(mt2, mt1); });
    t1.join();
    t2.join();
}

上述例子使用std::unique_lock其实也可以,但这时使用如上面的lock_guard效率更高。

方法二:先构建std::unique_lock,再同时lock掉两个锁

构建unique_lock时,Tag 参数为 std::defer_lock,表明不lock锁,在执行功能代码之前再统一lock掉两个unique_lock,在 unique_lock 的生命周期结束之后,mt对象自动解锁。

#include <iostream>
#include <chrono>
#include <mutex>
#include <thread>
#include <assert.h>
std::mutex mt1;
std::mutex mt2;
void deadLockProcess2(std::mutex& mtA, std::mutex& mtB)
{
    std::unique_lock<std::mutex>lock1(mtA, std::defer_lock);
    std::cout << "get the first mutex" << " in thread " << std::this_thread::get_id() << std::endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(1));

    std::unique_lock<std::mutex>lock2(mtB, std::defer_lock);
    std::cout << "get the second mutex" << " in thread " << std::this_thread::get_id() << std::endl;

    std::lock(lock1, lock2);

    assert(lock1.owns_lock() == true);


    std::cout << "do something in thread " << std::this_thread::get_id() << std::endl;
}

int main() {
    std::thread t1([&] {deadLockProcess2(mt1, mt2); });
    std::thread t2([&] {deadLockProcess2(mt2, mt1); });
    t1.join();
    t2.join();
}

相关文章

  • C++11_lock_guard的线程死锁问题和解决

    视频教程:https://www.bilibili.com/video/av92453755 std::lock_...

  • jstack命令:教你如何排查多线程问题

    这是之前的一个死锁案例: 一个多线程死锁案例,如何避免及解决死锁问题? 如程序中发生这样的死锁问题该如何排查呢?我...

  • 防止死锁的加锁机制

    防止死锁的加锁机制 问题 你正在写一个多线程程序,其中线程需要一次获取多个锁,此时如何避免死锁问题。 解决方案 在...

  • 线程 6. 死锁

    没有办法解决,只能避免 java同步机制解决了线程安全问题,但是同时也引发了死锁现象。死锁现象如何解决呢: 没法解...

  • 并发知识5

    死锁 锁和条件不能解决线程中的所有问题账户1:200; 账户2:300;线程1:从账户1转移300到账户2 线程2...

  • 中级09 - Java多线程初步

    中级09 - Java多线程初步 介绍多线程带来的问题,以及基本解决方案。 竞争条件带来的数据错误问题 死锁的原...

  • 面试官:连多线程问题你都一问三不知,还要我怎么“放水”?

    面试官:问你几个多线程相关的问题吧,说一下导致线程死锁的原因,怎么解除线程死锁? 程序员阿里:这个...死锁......

  • Java多线程之(五)线程的通信

    为了解决线程的死锁问题,引入线程通讯 1. 线程通信涉及到的三个方法: wait():一旦执行此方法,当前线程就进...

  • jvm监控

    jdk自带工具 jps jmap jhat jstat jstack 可解决的问题 内存不足,线程死锁,锁竞争,线...

  • Java多线程(下篇)

    一、死锁 1.同步锁 解决了线程安全问题,但会造成性能低下,还可能会引发一个问题(罕见):死锁 2.例子 a.一个...

网友评论

      本文标题:C++11_lock_guard的线程死锁问题和解决

      本文链接:https://www.haomeiwen.com/subject/pfzphhtx.html