C++:并发与多线程(十一)

一 windows临界区

windows下的临界区类似于C+11的mutex,都是进入临界区,执行函数,离开临界区

CRITICAL_SECTION  my_winsec;  //windows下的临界区 类似于C++11的mutex
InitializeCriticalSection(& my_winsec) ;  //windows下的临界区初始化
EnterCriticalSection(& my_winsec) ;  //windows下的进入临界区,相当于lock
LeaveCriticalSection(&my_winsec); //离开临界区

示例代码

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

#define __WINDOWS_




class A
{
public:
    //把玩家命令放入到一个队列的进程
    std::unique_lock<std::mutex>rtn_unique_lock()
    {
        std::unique_lock<std::mutex>temguard(my_mutex1);
        return temguard;

    }
    void inMsgRecvQueue()
    {
        for (int i = 0; i < 100; i++)
        {
            {
                cout << "inMsgRecvQueue insert:" << i << endl;
#ifdef __WINDOWS_
                EnterCriticalSection(& my_winsec) ;  //windows下的进入临界区,相当于lock
                msgRecvQueue.push_back(i);     //假设数字i为命令 放入队列
                LeaveCriticalSection(&my_winsec); //离开临界区
#endif

            }
        }
    }

    //读取命令的线程
    void outMsgRecvQueue() {
        int command = 0;
        while (true) {
#ifdef __WINDOWS_
            EnterCriticalSection(& my_winsec) ;  //windows下的进入临界区,相当于lock
            if(!msgRecvQueue.empty())
            {
                command = msgRecvQueue.front();
                msgRecvQueue.pop_front();
                LeaveCriticalSection(&my_winsec); //离开临界区
                cout<<"outMsgRecvQueue get :"<<command<<endl;
            }
            LeaveCriticalSection(&my_winsec); //离开临界区

#endif
        }
    }


    A()
    {
#ifdef __WINDOWS_
        InitializeCriticalSection(& my_winsec) ;  //windows下的临界区初始化
#endif
    }
private:

    std::list<int>msgRecvQueue;
    mutex my_mutex1;           //创建一个互斥量
    condition_variable my_cond;  //生成一个条件变量对象

#ifdef __WINDOWS_
    CRITICAL_SECTION  my_winsec;  //windows下的临界区 类似于C++11的mutex
#endif
};

int main()
{

    //Windows的临界区



    A myobja;
    thread myOutnMsg(&A::outMsgRecvQueue, &myobja);
    thread myInMsgObj(&A::inMsgRecvQueue, &myobja);
    myOutnMsg.join();
    myInMsgObj.join();
}


二 多次进入临界区试验

在同一个线程,windows中的“相同的临界区变量”代表的临界区允许多次进入,但是调用几次进入就要调用几次离开,在C++11中不允许这样操作

    //允许重复进入临界区 ,但必须进入几次,离开几次
                EnterCriticalSection(& my_winsec) ;  //windows下的进入临界区,相当于lock
                EnterCriticalSection(& my_winsec) ;  //windows下的进入临界区,相当于lock
                msgRecvQueue.push_back(i);     //假设数字i为命令 放入队列
                LeaveCriticalSection(&my_winsec); //离开临界区
                LeaveCriticalSection(&my_winsec); //离开临界区

示例:

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

#define __WINDOWS_




class A
{
public:
    //把玩家命令放入到一个队列的进程
    std::unique_lock<std::mutex>rtn_unique_lock()
    {
        std::unique_lock<std::mutex>temguard(my_mutex1);
        return temguard;

    }
    void inMsgRecvQueue()
    {
        for (int i = 0; i < 100; i++)
        {
            {
                cout << "inMsgRecvQueue insert:" << i << endl;
#ifdef __WINDOWS_
                //允许重复进入临界区 ,但必须进入几次,离开几次
                EnterCriticalSection(& my_winsec) ;  //windows下的进入临界区,相当于lock
                EnterCriticalSection(& my_winsec) ;  //windows下的进入临界区,相当于lock
                msgRecvQueue.push_back(i);     //假设数字i为命令 放入队列
                LeaveCriticalSection(&my_winsec); //离开临界区
                LeaveCriticalSection(&my_winsec); //离开临界区
#endif

            }
        }
    }

    //读取命令的线程
    void outMsgRecvQueue() {
        int command = 0;
        while (true) {
#ifdef __WINDOWS_
            EnterCriticalSection(& my_winsec) ;  //windows下的进入临界区,相当于lock
            if(!msgRecvQueue.empty())
            {
                command = msgRecvQueue.front();
                msgRecvQueue.pop_front();
                LeaveCriticalSection(&my_winsec); //离开临界区
                cout<<"outMsgRecvQueue get :"<<command<<endl;
            }
            LeaveCriticalSection(&my_winsec); //离开临界区

#endif
        }
    }


    A()
    {
#ifdef __WINDOWS_
        InitializeCriticalSection(& my_winsec) ;  //windows下的临界区初始化
#endif
    }
private:

    std::list<int>msgRecvQueue;
    mutex my_mutex1;           //创建一个互斥量
    condition_variable my_cond;  //生成一个条件变量对象

#ifdef __WINDOWS_
    CRITICAL_SECTION  my_winsec;  //windows下的临界区 类似于C++11的mutex
#endif
};

int main()
{

    //一Windows的临界区

    //二 多次进去临界区
//在同一个线程,windows中的“相同的临界区变量”代表的临界区允许多次进入,
// 但是调用几次进入就要调用几次离开,在C++11中不允许这样操作

    A myobja;
    thread myOutnMsg(&A::outMsgRecvQueue, &myobja);
    thread myInMsgObj(&A::inMsgRecvQueue, &myobja);
    myOutnMsg.join();
    myInMsgObj.join();
}

三 自动析构技术

在C++11中,使用std::lock_guardstd::mutexsbguard(my_mutex);可以在当前程序段执行结束自动解锁

 std::lock_guard<std::mutex>sbguard(my_mutex1);
 msgRecvQueue.push_back(i);
 std::lock_guard<std::mutex>sbguard(my_mutex1);
            if(!msgRecvQueue.empty())
            {
                command = msgRecvQueue.front();
                msgRecvQueue.pop_front();
                cout<<"outMsgRecvQueue get :"<<command<<endl;
            }

示例

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

//#define __WINDOWS_




class A
{
public:
    //把玩家命令放入到一个队列的进程
    std::unique_lock<std::mutex>rtn_unique_lock()
    {
        std::unique_lock<std::mutex>temguard(my_mutex1);
        return temguard;

    }
    void inMsgRecvQueue()
    {
        for (int i = 0; i < 100; i++)
        {
            {
                cout << "inMsgRecvQueue insert:" << i << endl;
#ifdef __WINDOWS_
                //允许重复进入临界区 ,但必须进入几次,离开几次
                EnterCriticalSection(& my_winsec) ;  //windows下的进入临界区,相当于lock
                EnterCriticalSection(& my_winsec) ;  //windows下的进入临界区,相当于lock
                msgRecvQueue.push_back(i);     //假设数字i为命令 放入队列
                LeaveCriticalSection(&my_winsec); //离开临界区
                LeaveCriticalSection(&my_winsec); //离开临界区
#else
 std::lock_guard<std::mutex>sbguard(my_mutex1);
 msgRecvQueue.push_back(i);
#endif

            }
        }
    }

    //读取命令的线程
    void outMsgRecvQueue() {
        int command = 0;
        while (true) {
#ifdef __WINDOWS_
            EnterCriticalSection(& my_winsec) ;  //windows下的进入临界区,相当于lock
            if(!msgRecvQueue.empty())
            {
                command = msgRecvQueue.front();
                msgRecvQueue.pop_front();
                LeaveCriticalSection(&my_winsec); //离开临界区
                cout<<"outMsgRecvQueue get :"<<command<<endl;
            }
            LeaveCriticalSection(&my_winsec); //离开临界区

#else
            std::lock_guard<std::mutex>sbguard(my_mutex1);
            if(!msgRecvQueue.empty())
            {
                command = msgRecvQueue.front();
                msgRecvQueue.pop_front();
                cout<<"outMsgRecvQueue get :"<<command<<endl;
            }

#endif
        }
    }

    A()
    {
#ifdef __WINDOWS_
        InitializeCriticalSection(& my_winsec) ;  //windows下的临界区初始化
#endif
    }
private:

    std::list<int>msgRecvQueue;
    mutex my_mutex1;           //创建一个互斥量
    condition_variable my_cond;  //生成一个条件变量对象

#ifdef __WINDOWS_
    CRITICAL_SECTION  my_winsec;  //windows下的临界区 类似于C++11的mutex
#endif
};

int main()
{
    A myobja;
    thread myOutnMsg(&A::outMsgRecvQueue, &myobja);
    thread myInMsgObj(&A::inMsgRecvQueue, &myobja);
    myOutnMsg.join();
    myInMsgObj.join();
}

而在Windows下,如果我们想实现这个功能,可以借助类对象的析构来完成
该类称为RAII类,即 资源获取即初始化
容器,智能指针都称为RAII类 其对象称为 RAII对象

//本类用于自动释放windows下的临界区,防止忘记离开临界区导致的死锁,类似于C++11的lock_guard
class CWinLock
{
public:
    CWinLock(CRITICAL_SECTION *pcr)
    {
        m_pCritical  = pcr;
        //进入临界区
        EnterCriticalSection(m_pCritical);
    }
    ~CWinLock()
    {
        LeaveCriticalSection(m_pCritical); //离开临界区

    }


private:
    CRITICAL_SECTION* m_pCritical;
};

在需要保护的临界区前初始化一个该类的对象,程序结束后该对象自动析构,会调用析构函数,析构函数内离开临界区,并且多次创建也没问题,多次创建对象就会多次析构对象,就会多次离开临界区。

          CWinLock cwlock(&my_winsec);
                msgRecvQueue.push_back(i);     //假设数字i为命令 放入队列

示例

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

#define __WINDOWS_

//本类用于自动释放windows下的临界区,防止忘记离开临界区导致的死锁,类似于C++11的lock_guard
class CWinLock
{
public:
    CWinLock(CRITICAL_SECTION *pcr)
    {
        m_pCritical  = pcr;
        //进入临界区
        EnterCriticalSection(m_pCritical);
    }
    ~CWinLock()
    {
        LeaveCriticalSection(m_pCritical); //离开临界区

    }


private:
    CRITICAL_SECTION* m_pCritical;
};

class A
{
public:
    //把玩家命令放入到一个队列的进程
    std::unique_lock<std::mutex>rtn_unique_lock()
    {
        std::unique_lock<std::mutex>temguard(my_mutex1);
        return temguard;

    }
    void inMsgRecvQueue()
    {
        for (int i = 0; i < 100; i++)
        {
            {
                cout << "inMsgRecvQueue insert:" << i << endl;
#ifdef __WINDOWS_
                //允许重复进入临界区 ,但必须进入几次,离开几次
//                EnterCriticalSection(& my_winsec) ;  //windows下的进入临界区,相当于lock
//                EnterCriticalSection(& my_winsec) ;  //windows下的进入临界区,相当于lock
                CWinLock cwlock(&my_winsec);
                msgRecvQueue.push_back(i);     //假设数字i为命令 放入队列
//                LeaveCriticalSection(&my_winsec); //离开临界区
//                LeaveCriticalSection(&my_winsec); //离开临界区
#else
 std::lock_guard<std::mutex>sbguard(my_mutex1);
 msgRecvQueue.push_back(i);
#endif

            }
        }
    }

    //读取命令的线程
    void outMsgRecvQueue() {
        int command = 0;
        while (true) {
#ifdef __WINDOWS_
            CWinLock cwlock(&my_winsec);
            if(!msgRecvQueue.empty())
            {
                command = msgRecvQueue.front();
                msgRecvQueue.pop_front();
                cout<<"outMsgRecvQueue get :"<<command<<endl;
            }

#else
            std::lock_guard<std::mutex>sbguard(my_mutex1);
            if(!msgRecvQueue.empty())
            {
                command = msgRecvQueue.front();
                msgRecvQueue.pop_front();
                cout<<"outMsgRecvQueue get :"<<command<<endl;
            }

#endif
        }
    }


    A()
    {
#ifdef __WINDOWS_
        InitializeCriticalSection(& my_winsec) ;  //windows下的临界区初始化
#endif
    }
private:

    std::list<int>msgRecvQueue;
    mutex my_mutex1;           //创建一个互斥量
    condition_variable my_cond;  //生成一个条件变量对象

#ifdef __WINDOWS_
    CRITICAL_SECTION  my_winsec;  //windows下的临界区 类似于C++11的mutex
#endif
};

int main()
{

    A myobja;
    thread myOutnMsg(&A::outMsgRecvQueue, &myobja);
    thread myInMsgObj(&A::inMsgRecvQueue, &myobja);
    myOutnMsg.join();
    myInMsgObj.join();
}


四 recursive mutex递归的独占互斥量

当使用普通mutex上锁时,如果同一线程的同一个锁被多次lock,会出现异常,C++11不允许这么做
mutex 称为独占互斥量
例如

  void testFunc1()
    {
        std::lock_guard<std::mutex>sbguard(my_mutex1);
        cout<<"testFunc1"<<endl;
        testFunc2(); //调用两次my_mutex1,系统崩溃
    }
    void testFunc2()
    {
        std::lock_guard<std::mutex>sbguard(my_mutex1);
        cout<<"testFunc2"<<endl;
        testFunc3();
    }
    void testFunc3()
    {
        std::lock_guard<std::mutex>sbguard(my_mutex1);
        cout<<"testFunc3"<<endl;
    }
    A myobja;
    thread myobj(&A::testFunc1, &myobja);
    myobj.join();
}

打印结果:程序卡在Func1无法执行

G:\CSlrn\C++Code\MultiThread\2_12\cmake-build-debug\2_12.exe
testFunc1

recursive_mutex允许同一个线程的同一个互斥量多次被lock
我们将mutex改为recursive_mutex,程序可以正常运行
示例

#include <iostream>
#include <vector>
#include <thread>
#include <list>
#include  <mutex>
#include <condition_variable>
#include <windows.h>
using namespace std;
class A
{
public:
void testFunc1()
    {
        std::lock_guard<std::recursive_mutex>sbguard(my_mutex1);
        cout<<"testFunc1"<<endl;
        testFunc2(); //调用两次my_mutex1,系统崩溃
    }
    void testFunc2()
    {
        std::lock_guard<std::recursive_mutex>sbguard(my_mutex1);
        cout<<"testFunc2"<<endl;
        testFunc3();
    }
    void testFunc3()
    {
        std::lock_guard<std::recursive_mutex>sbguard(my_mutex1);
        cout<<"testFunc3"<<endl;
    }
    private:

    recursive_mutex my_mutex1;           //创建一个递归独占互斥锁
 };

 int main()
{  
    A myobja;
    thread myobj(&A::testFunc1, &myobja);
    myobj.join();
}
 

打印结果
程序不会发生异常,即使是将my_mutex1多次调用,程序正常执行

G:\CSlrn\C++Code\MultiThread\2_12\cmake-build-debug\2_12.exe
testFunc1
testFunc2
testFunc3

Process finished with exit code 0

其缺点是效率比mutex差一些,递归太多可能会出现异常。

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

  • timed_mutex带超时的独占互斥量
  • timed_mutex提供了两个接口:
    1.try_lock_for();等待一段时间,如果拿到了锁或者超时没拿到锁,那么流程继续往下走
    2.try_lock_until(); 参数为某一个未来的时间点,在达到该时间点前,拿到了锁则执行任务函数,程序往下走,如果没拿到,程序也往下走,不卡住。
  • recursive_timed_mutex 带超时的递归独占互斥量,recursive_timed_mutex 带超时的递归独占互斥量 与recursive_mutex功能类似,其带有超时功能

1. timed_mutex的try_lock_for()接口

测试用例:将outmsg函数的锁拿一个非常久的时间,观察inmsg的拿锁情况
outmsg函数内:

 std::lock_guard<std::timed_mutex>sbguard(my_mutex1);
            //拿到锁不放开 测试timed_mutex功能
            std::chrono::milliseconds sleeptime(100000);

inmsg函数内:拿到锁了则执行任务函数,没拿到锁则进行接下来的操作,程序不会像独占互斥量那样卡在一个地方

std::chrono::microseconds timeout(100);
 if (my_mutex1.try_lock_for(timeout))
 {
     //在100ms内拿到了锁
     cout<<"get_mutex"<<endl;
     msgRecvQueue.push_back(i);
     my_mutex1.unlock();
 }
 else //没拿到锁
 {
     cout<<"not get_mutex"<<endl;
     std::chrono::microseconds sleeptime(100);
     std::this_thread::sleep_for(sleeptime);
 }
 cout<<"go on"<<endl;

完整例子

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

//#define __WINDOWS_
class CWinLock
{
public:
    CWinLock(CRITICAL_SECTION *pcr)
    {
        m_pCritical  = pcr;
        //进入临界区
        EnterCriticalSection(m_pCritical);
    }
    ~CWinLock()
    {
        LeaveCriticalSection(m_pCritical); //离开临界区

    }


private:
    CRITICAL_SECTION* m_pCritical;
};

class A
{
public:
    //把玩家命令放入到一个队列的进程
    std::unique_lock<std::timed_mutex>rtn_unique_lock()
    {
        std::unique_lock<std::timed_mutex>temguard(my_mutex1);
        return temguard;

    }
    void inMsgRecvQueue()
    {
        for (int i = 0; i < 100; i++)
        {
            {
                cout << "inMsgRecvQueue insert:" << i << endl;
#ifdef __WINDOWS_
                CWinLock cwlock(&my_winsec);  //cwlock RAII对象
                msgRecvQueue.push_back(i);     //假设数字i为命令 放入队列

#else
 std::chrono::microseconds timeout(100);
 if (my_mutex1.try_lock_for(timeout))
 {
     //在100ms内拿到了锁
     cout<<"get_mutex"<<endl;
     msgRecvQueue.push_back(i);
     my_mutex1.unlock();
 }
 else //没拿到锁
 {
     cout<<"not get_mutex"<<endl;
     std::chrono::microseconds sleeptime(100);
     std::this_thread::sleep_for(sleeptime);
 }
cout<<"go on"<<endl;
#endif

            }
        }
    }

    //读取命令的线程
    void outMsgRecvQueue() {
        int command = 0;
        while (true) {
#ifdef __WINDOWS_
            CWinLock cwlock(&my_winsec);
            if(!msgRecvQueue.empty())
            {
                command = msgRecvQueue.front();
                msgRecvQueue.pop_front();
                cout<<"outMsgRecvQueue get :"<<command<<endl;
            }

#else
            std::lock_guard<std::timed_mutex>sbguard(my_mutex1);
            //拿到锁不放开 测试timed_mutex功能
            std::chrono::milliseconds sleeptime(100000);
            std::this_thread::sleep_for(sleeptime);
            if(!msgRecvQueue.empty())
            {
                command = msgRecvQueue.front();
                msgRecvQueue.pop_front();
                cout<<"outMsgRecvQueue get :"<<command<<endl;
            }

#endif
        }
    }


    A()
    {
#ifdef __WINDOWS_
        InitializeCriticalSection(& my_winsec) ;  //windows下的临界区初始化
#endif
    }

private:

    std::list<int>msgRecvQueue;
    std::timed_mutex my_mutex1;

#ifdef __WINDOWS_
    CRITICAL_SECTION  my_winsec;  //windows下的临界区 类似于C++11的mutex
#endif
};

int main()
{

    A myobja;
    thread myOutnMsg(&A::outMsgRecvQueue, &myobja);
    thread myInMsgObj(&A::inMsgRecvQueue, &myobja);
    myOutnMsg.join();
    myInMsgObj.join();
}

打印结果:try_lock_for一直在尝试拿锁,拿到锁则执行任务函数,执行完程序继续往下走
如果在100us没拿到锁,则程序还是继续往下执行。

G:\CSlrn\C++Code\MultiThread\2_12\cmake-build-debug\2_12.exe
inMsgRecvQueue insert:0
not get_mutex
go on
inMsgRecvQueue insert:1
not get_mutex
go on
inMsgRecvQueue insert:2
not get_mutex
go on
inMsgRecvQueue insert:3
not get_mutex
go on
inMsgRecvQueue insert:4
not get_mutex
go on
inMsgRecvQueue insert:5
not get_mutex
go on
.
.
.
.
inMsgRecvQueue insert:97
not get_mutex
go on
inMsgRecvQueue insert:98
not get_mutex
go on
inMsgRecvQueue insert:99
not get_mutex
go on
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Michael.Scofield

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值