c++ 递归锁

递归锁RECURSIVE_MUTEX的原理以及使用

标签: Multi_Thread  Recursive_Mutex

递归锁的作用

MutexLock mutex;  

void foo()  
{  
    mutex.lock();  
    // do something  
    mutex.unlock();  
}  

void bar()  
{  
    mutex.lock();  
    // do something  
    foo();  
    mutex.unlock();   
}  

foo函数和bar函数都获取了同一个锁,而bar函数又会调用foo函数。如果MutexLock锁是个非递归锁,则这个程序会立即死锁。因此在为一段程序加锁时要格外小心,否则很容易因为这种调用关系而造成死锁。
不要存在侥幸心理,觉得这种情况是很少出现的。当代码复杂到一定程度,被多个人维护,调用关系错综复杂时,程序中很容易犯这样的错误。庆幸的是,这种原因造成的死锁很容易被排除。
但是这并不意味着应该用递归锁去代替非递归锁。递归锁用起来固然简单,但往往会隐藏某些代码问题。比如调用函数和被调用函数以为自己拿到了锁,都在修改同一个对象,这时就很容易出现问题。因此在能使用非递归锁的情况下,应该尽量使用非递归锁,因为死锁相对来说,更容易通过调试发现。程序设计如果有问题,应该暴露的越早越好。

本段参考链接


自制递归锁

#include"stdafx.h"
#include <iostream>
#include <chrono>
#include <thread>
#include <mutex>
#include <stdio.h>
#include <Windows.h>

using namespace std;

int g_num = 0;  // 为 g_num_mutex 所保护
recursive_mutex g_num_mutex;

class RecursiveMutex {
public:
    RecursiveMutex() {
        num_of_locks = 0;
    }

    ~RecursiveMutex() {

    }

    void lock() {
        if (num_of_locks == 0) {
            my_mutex.lock();
            owner_thread_id = GetCurrentThreadId();
        }
        else if (GetCurrentThreadId() == owner_thread_id)
            num_of_locks++;
    }

    void unlock() {
        if (num_of_locks > 0)
            num_of_locks--;
        if (num_of_locks == 0)
            my_mutex.unlock();
    }

private:
    int num_of_locks;
    mutex my_mutex;
    int owner_thread_id;
};

RecursiveMutex myRecursiveMutex;

void slow_increment_stl(int id)
{
    for (int i = 0; i < 3; ++i) {
        g_num_mutex.lock();
        ++g_num;
        std::cout << id << " => " << g_num << '\n';
        g_num_mutex.unlock();

        std::this_thread::sleep_for(std::chrono::seconds(2));
    }
}

void slow_increment(int id)
{
    for (int i = 0; i < 3; ++i) {
        myRecursiveMutex.lock();
        ++g_num;
        std::cout << id << " => " << g_num << '\n';
        myRecursiveMutex.unlock();

        std::this_thread::sleep_for(std::chrono::seconds(2));
    }
}

int main()
{
    cout << "使用stl中的recursive_mutex" << endl;
    std::thread t1(slow_increment_stl, 0);
    std::thread t2(slow_increment_stl, 1);
    t1.join();
    t2.join();

    cout << "使用自写类RecursiveMutex" << endl;
    std::thread t3(slow_increment, 0);
    std::thread t4(slow_increment, 1);
    t3.join();
    t4.join();

}

程序输出

可见使用自制递归锁类与标准库中的递归锁类输出相同。

 

问题就是:加锁的操作需要相互嵌套,如果使用std::mutex 肯定会导致死锁,而重构代码,提取出共用部分的工作量又很大。

这个时候我发现了好东西 std::recursive_mutex 递归锁

递归锁可以允许一个线程对同一互斥量多次加锁,解锁时,需要调用与lock()相同次数的unlock()才能释放使用权

这边再介绍一个好东西:

std::lock_guard<std::recursive_mutex> 

std::lock_guard在构造函数中加锁,在析构函数中解锁,利用这个类可以减少我们对加锁可解锁操作的管理工作,专注于逻辑实现。

lock_guard类结构如下

template<typename _Mutex>
    class lock_guard
    {
    public:
      typedef _Mutex mutex_type;

      explicit lock_guard(mutex_type& __m) : _M_device(__m)
      { _M_device.lock(); }

      lock_guard(mutex_type& __m, adopt_lock_t) : _M_device(__m)
      { } // calling thread owns mutex

      ~lock_guard()
      { _M_device.unlock(); }

      lock_guard(const lock_guard&) = delete;
      lock_guard& operator=(const lock_guard&) = delete;

    private:
      mutex_type&  _M_device;
    };

可以很清楚的看到加锁和解锁过程
 

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【问题描述】 一个国王因为听信谗言将一个无辜的数学家关进了监狱。虽然事后发现确属冤枉,但碍于面子,国王不肯认错。为了挽回,于是国王决定用Bytish链将其在墙上。这种链由n(10≤n≤1000)个固定在墙上的铁环和铁棒组成。由于环不是都套在棒上,要想把整副链取下是十分困难的。数学家必须自己通过不断取下和套上铁环最终将所有铁环都取下才能获得自由。取下或套上铁环的规则是:  铁环从1、2、……、n依次编号。  一次只能把一个环取下或套上。  编号为1的环无论何时都能取下或套上。  如果编号为1、……、k-1(1≤k≤n)的环已经从棒上取下,并且k环套在棒上,则可以取下或套上编号为k+1的环。 写一个程序,读入链描述并计算从棒上取下所有环所需的最少步数。 【基本要求】 显然,可以运用递归的方法解决此问题。但是你能否找到一个非递归算法呢? 【输入输出】 输入:环的总数n。 输出:为尽量体现程序输出结果的层次,要求按照从n、n-1、n-2、……、1的顺序,将移除掉n号环的全部过程作为一个段落输出,然后将移除n-1号环的全部过程也作为一个段落输出,其余依此类推。 【实现提示】 显然,通过枚举前i个环的解过程可以从中找出解题的思路。 当n=1时,直接移除即得解。 当n=2时,显然不能先移出1环,再移除2环。因为根据约束条件,必须1环在棒上才能移除2环。因此移除的步骤应该是先2环,再1环。 当n=3时,移除3环的条件是2环在,1环卸,于是可以先移除1环,然后移除3环,然后套上1环,然后移除2环和1环。 当n≥4的情形请自行推导。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值