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

2020-02-29  本文已影响0人  JasonLiThirty

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

std::lock_guard

std::unique_lock

两者区别

线程死锁

因为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();
}

上一篇下一篇

猜你喜欢

热点阅读