c++11并发与多线程【第十二节】windows临界区、其他各种mutex互斥量

【第十二节】windows临界区、其他各种mutex互斥量

一、windows临界区

#include<iostream>
#include<vector>
#include<thread>
#include<string>
#include<list>
#include<mutex>
#include<windows.h>
using namespace std;
#define _WINDOWSJQ_

class A{
public:
    //把收到的消息(玩家命令)放入到一个队列的线程入口函数
    void inMsgRecvQueue(){
        for(int i=0;i<10000;i++){//用数字模拟玩家发送来的命令
            cout<<"inMsgRecvQueue()执行,插入一个元素 "<<i<<endl;
#ifdef _WINDOWSJQ_
            EnterCriticalSection(&my_winsec);
            msgRecvQueue.push_back(i);
            LeaveCriticalSection(&my_winsec);
#else
            //
            my_mutex.lock();//加锁
            msgRecvQueue.push_back(i);//把命令放入队列当中
            my_mutex.unlock();//解锁
            //
#endif
        }
    }

    bool MsgProcess(int &command){
#ifdef _WINDOWSJQ_
        EnterCriticalSection(&my_winsec);//进入临界区
        if(!msgRecvQueue.empty()){
            command=msgRecvQueue.front();//返回第一个元素
            msgRecvQueue.pop_front();//取出后移除该元素
            LeaveCriticalSection(&my_winsec);
            //然后处理数据
            return true;
        }
        LeaveCriticalSection(&my_winsec);//离开临界区
#else
        my_mutex.lock();//加锁
        if(!msgRecvQueue.empty()){
            command=msgRecvQueue.front();//返回第一个元素
            msgRecvQueue.pop_front();//取出后移除该元素
            my_mutex.unlock();//解锁
            //然后处理数据
            return true;
        }
        my_mutex.unlock();//解锁
#endif
        return false;
    }
    //从消息队列list中读取玩家命令的线程入口函数
    void outMsgRecvQueue(){
        int command=0;
        for(int i=0;i<10000;i++){
            //
            bool result=MsgProcess(command);
            if(result){
                cout<<"outMsgRecvQueue执行,取出一个元素 "<<command<<endl;
                //然后对数据进行处理
            }
            else
                cout<<"outMsgRecvQueue执行,但是list已经空了 : "<<i<<endl;
        }
        cout<<"end "<<endl;
    }
A()
{
#ifdef _WINDOWSJQ_
    IntialiseCriticalSection(&my_winsec);//初始化临界区
#endif
}
private:
    std::list<int> msgRecvQueue;//在list中存放玩家发来的命令
    std::mutex my_mutex;

#ifdef _WINDOWSJQ_
    CRITICAL_SECTION my_winsec;//windows中的临界区,类似与c++11中的mutex

#endif
};

int main(){
    A myobj;
    std::thread myInMsgObj(&A::outMsgRecvQueue,&myobj);//第二个参数是引用,作用与std::ref相同,保证是子线程中使用的是主线程中的同一个对象,但是主线程后面必须等待子线程完成
    std::thread myOutMsgObj(&A::inMsgRecvQueue,&myobj);
    myInMsgObj.join();
    myOutMsgObj.join();

    cout<<"主线程结束"<<endl;
    return 0;
}

上面的代码在windows环境下与使用mutex来加锁解锁完成的功能相同。

二、多次进入临界区实验

在同一个线程中,多次进入临界区是允许的,windows中的“”相同临界区变量“”代表的临界区的进入(EnterCriticalSection(&my_winsec);)可以被多次调用,但是调用了多少次,就得调用多少次LeaveCriticalSection(&my_winsec); 但是mutex类的lock是不允许在unlock之前多次执行的。

#ifdef _WINDOWSJQ_
            EnterCriticalSection(&my_winsec);//进入临界区(加锁)
            EnterCriticalSection(&my_winsec);//两次进入
            msgRecvQueue.push_back(i);
            LeaveCriticalSection(&my_winsec);//离开临界区(解锁)
            LeaveCriticalSection(&my_winsec);//再次离开
#else

三、自动析构技术

#include<windows.h>
using namespace std;
#define _WINDOWSJQ_
//本类用于自动释放windows下的临界区,防止忘记LeaveCriticalSection,导致死锁,类似于C++11中的lock_guard
class CWinLock //RAII类(Resource Acquisition Is Initialization)中文“资源获取即初始化”
{              //容器、智能指针都属于RAII类
public:
    CWinLock(CRITICAL_SECTION *pCritmp)//构造函数
    {
        m_pCritical=pCritmp;
        EnterCriticalSection(m_pCritical);
    }
    ~CWinLock()//析构函数
    {
        EnterCriticalSection(m_pCritical);
    }
private:
    CRITICAL_SECTION *m_pCritical;
};

    //把收到的消息(玩家命令)放入到一个队列的线程入口函数
void inMsgRecvQueue(){
        for(int i=0;i<10000;i++){//用数字模拟玩家发送来的命令
            cout<<"inMsgRecvQueue()执行,插入一个元素 "<<i<<endl;
#ifdef _WINDOWSJQ_
            //EnterCriticalSection(&my_winsec);//进入临界区(加锁)
            //EnterCriticalSection(&my_winsec);//两次进入
            CWinLock wlcok(&my_winsec);
            CWinLock wlcok2(&my_winsec);//进入临界区多次也没问题
            msgRecvQueue.push_back(i);
            //LeaveCriticalSection(&my_winsec);//离开临界区(解锁)
            //LeaveCriticalSection(&my_winsec);//再次离开
#else
            //
            //my_mutex.lock();//加锁
            std::lock_guard<std::mutex> sbguard(my_mutex);
            msgRecvQueue.push_back(i);//把命令放入队列当中
            //my_mutex.unlock();//解锁
            //
#endif
        }
    }

四、recursive_mutex递归的独占互斥量

std::mutex 独占互斥量,一次只有一个线程拿到锁;
recursive_mutex 递归的独占互斥量,允许同一个线程,同一个互斥量多次被lock;

//std::mutex my_mutex;
std::recursive_mutex my_mutex;//递归独占互斥量

使用了这种多次lock的互斥量,要考虑代码是否有优化空间,递归太多可能报异常;

五、带超时的互斥量std::timed_mutex和std::recursive_timed_mutex

5.1 std::timed_mutex:是带超时功能的独占互斥量

try_lock_for():参数是一段时间,等待一段时间,如果拿到了锁或者等待超过时间没拿到锁,就往下走,不会一直阻塞;

std::timed_mutex my_mutex;
std::chrono::milliseconds timeout(100);//100毫秒
if(my_mutex.try_lock_for(timeout))//等待100毫秒来尝试获取锁
{
    //在这100毫秒之内拿到了锁
    //操作
    my_mutex.unlock();//用完了要解锁
}
else
{
}

try_lock_until() :参数是一个未来的时间点,在这个未来的时间没有到达的时间内,如果拿到了锁或者超时,就往下走;

if(my_mutex.try_lock_until(std::chrono::steady_lock::now()*timeout))//从当前时间算起的100毫秒
{
    //在这100毫秒之内拿到了锁
    //操作
    my_mutex.unlock();//用完了要解锁
}

5.2 std::recursive_timed_mutex 带超时功能的递归独占互斥量

文章内容来源《C++并发与多线程视频课程》

  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2022-2024 lk
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信