C++多线程:RAII技术、recursive_mutex、timed_mutex(十二)

1、Windows临界区
  • Window也提供了类似于C++11多线程库中的std::mutex互斥量锁,被叫做关键区域(CRITICAL_SECTION),在头文件Windows.h中可以找到
    • CRITICAL_SECTION类型:定义一个关键区域变量my_sec
    • InitializeCriticalSection(&my_sec);初始化这个变量
    • 当需要对一段代码段进行串行执行(线程安全)时,可以通过EnterCriticalSection(&my_sec);来加锁
    • 访问结束通过LeaveCriticalSection(&my_sec);释放这个临界区域的锁
// Recursive_Timed_Mutex.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <mutex>
#include <queue>
#include <vector>
#include <future>
#include <Windows.h>

#define __WINDOWS_MUTEX__
#define maxn 100000
class MessageQueue {
public:
    MessageQueue() {
#ifdef __WINDOWS_MUTEX__
        InitializeCriticalSection(&my_sec);
#endif
    }

    void inMsg(){
        for (int i = 0; i < maxn; i++) {
#ifdef __WINDOWS_MUTEX__
            EnterCriticalSection(&my_sec);
            q.push(i);
            std::cout << "放入元素, msg = " << i << std::endl;
            LeaveCriticalSection(&my_sec);
#endif
//            mutex_lock.lock();
//            q.push(i);
//            std::cout << "放入元素, msg = " << i << std::endl;
//            mutex_lock.unlock();
        }
    }
    void outMsg(){
        for (int i = 0; i < maxn; i++) {
#ifdef __WINDOWS_MUTEX__
            EnterCriticalSection(&my_sec);
//            EnterCriticalSection(&my_sec);
            get_msg();
            LeaveCriticalSection(&my_sec);
//            LeaveCriticalSection(&my_sec);
#endif 
//            mutex_lock.lock();
//            get_msg();
//            mutex_lock.unlock();
        }
    }
    
    void get_msg(){
        if (q.size()) {
            int msg = q.front();
            q.pop();
            std::cout << "msg = " << msg << std::endl;
        }
        else {
            std::cout << "没有取到数据" << std::endl;
        }
    }
private:
    std::queue<int> q;
    std::mutex mutex_lock;
#ifdef __WINDOWS_MUTEX__
    CRITICAL_SECTION my_sec;
#endif
};

void test1_CRITICAL_SECTION() {
    MessageQueue messageQueue;
    std::thread inthread(&MessageQueue::inMsg, &messageQueue);
    std::thread outthread(&MessageQueue::outMsg, &messageQueue);
    outthread.join();
    inthread.join();
}

int main()
{
    test1_CRITICAL_SECTION();

    std::cout << "Hello World!\n" << std::endl;
    return 0;
}
  • 但这个关键区域(CRITICAL_SECTION)与互斥量(Mutex)存在一定的差异,关键区域是可以进行锁重入的,而Mutex并不支持。

    • 锁重入:同一个线程可以对同一把锁多次获取
    • 获取多少次需要对应的释放多少次
    EnterCriticalSection(&my_sec);
    //	EnterCriticalSection(&my_sec);
    get_msg();
    LeaveCriticalSection(&my_sec);
    //	LeaveCriticalSection(&my_sec);
    
2、RAII技术
  • RAII:全称Resource Acquisition is Initialization,资源获取即初始化,这个东西在C++11中还是很常见的一个东西。
  • 例如多线程中的std::lock_guard模板类,在默认情况下传入一个mutex构造时就自动加锁,析构时自动释放锁
  • 底层源码中可以看到构造函数里自动对传入的mutex进行lock,而在析构函数时自动unlock。
  • lock_guard还是存在一定问题的,无法提前释放锁,需要等到这个模板类析构时才释放,如果需要在析构之前释放那么析构就会报错(锁的重复释放)
  • 对应的unique_lock也是一个RAII,但是unique_lock不仅含有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) noexcept : _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;
};
3、递归互斥量与超时互斥量

为了实现锁的重入和超时等待,C++11中同时也引入了可重入和超时mutex:

  • std::recursive_mutex
  • std::timed_mutex
  • std::recursive_timed_mutex
3.1、std::recursive_mutex
  • recursive_mutex递归的独占互斥量(可重入锁),允许同一个线程对同一个互斥量进行多次lock,效率上比mutex要差一些,重入的次数有限制,次数太多可能报异常

  • 其实就是一个可以重复加锁的mutex互斥量

  • 在使用时应该尽量的减少这个锁的使用,应该优先考虑程序的优化

例如下面这段代码,递归的计算1 + 2 + … + n的值,为了防止锁被占用导致多线程的错误采用可重入锁,其实这种情况应该采用单互斥量 + 循环或者 + 等差数列计算公式来做这个数列求和。

int f(int n){
    std::unique_lock<std::recursive_mutex> uniqueLock;
    if(n == 1){
        return 1;
    }
    return n + f(n - 1);
}
3.2、std::timed_mutex
  • 带超时功能的独占互斥量,防止出现死等的情况
  • timed_mutex主要提供了两种方法来检测超时
    • try_lock_for:等待一个指定的时间长度,如5秒
    • try_lock_until:等到到一个未来的时间点,如当前11点,等到12点。
std::timed_mutex timedMutex;
int a = 10;
void inMsg()
{
    timedMutex.lock();
    std::chrono::seconds sleeptime(3);
    std::this_thread::sleep_for(sleeptime);
    a = 2000;
    timedMutex.unlock();
}

void outMsg()
{
    std::chrono::seconds wait_time(5);
    if(timedMutex.try_lock_for(wait_time)){
        std::cout << "等到了!a = " << a << std::endl;
        timedMutex.unlock();
    }
    else {
        std::cout << "没等到!" << std::endl;
    };
}

void test_timed_mutex()
{
    std::thread inthread(inMsg);
    std::thread outthread(outMsg);
    inthread.join();
    outthread.join();
}
3.3、std::recursive_timed_mutex

带超时功能的递归互斥量,std::recursive_mutex和std::timed_mutex的结合版本,也没什么新奇的东西。基本上就是Mutex衍生的东西。

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值