Windows临界区、其他各种mutex互斥量
一 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