• 17.12 windows临界区、其他各种mutex互斥量


    初始代码如下

    class MA
    {
    public:
    	//把收到的消息(玩家命令)放入到一个队列的线程
    	void inMsgRecvQueue()
    	{
    		for (int i = 0; i < 100000; ++i)
    		{
    			cout << "inMsgRecvQueue()执行,插入一个元素: " << i << endl;
    			my_mutex.lock();
    			msgRecvQueue.push_back(i);  //假设这个数字i就是我收到的命令,直接弄到消息队列中
    			my_mutex.unlock();
    		}
    	}
    
    	bool outMsgLUProc(int& command)
    	{
    		my_mutex.lock();
    		if (!msgRecvQueue.empty())
    		{
    			//消息不为空
    			command = msgRecvQueue.front();  //返回第一个元素,但不检查元素是否存在;
    			msgRecvQueue.pop_front();  //移除第一个元素,但不返回
    			my_mutex.unlock();
    			return true;
    	}
    		my_mutex.unlock();
    		return false;
    }
    
    	//把数据从消息队列中取出的线程
    	void outMsgRecvQueue()
    	{
    		int command = 0;
    		for (int i = 0; i < 100000; ++i)
    		{
    			bool result = outMsgLUProc(command);
    			if (result == true)
    			{
    				cout << "outMsgRecvQueue()执行,取出一个元素" << endl;
    				//这里就考虑处理数据...
    				//...
    			}
    			else
    			{
    				cout << "outMsgRecvQueue()执行,但目前消息队列中为空 " << i << endl;
    			}
    		}
    		cout << "end" << endl;
    	}
    
    private:
    	list<int> msgRecvQueue;  //容器(消息队列),专门用于代表玩家发送的命令
    	std::mutex my_mutex;  //创建了一个互斥量
    };
    
    int main()
    {
    	MA myobj;
    	std::thread myOutMsgObj(&MA::outMsgRecvQueue, &myobj);  //第二个参数是引用,才能保证线程里用的是同一个对象
    	std::thread myInMsgObj(&MA::inMsgRecvQueue, &myobj);
    	myOutMsgObj.join();
    	myInMsgObj.join();
    	cout << "main主函数执行结束" << endl;  //最后执行这句,整个进程退出
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66

    一:windows临界区

    #include <list>
    #include <future>
    #include <iostream>
    #include <windows.h>
    
    using namespace std;
    
    #define __WINDOWSJQ_  //定义一个开关,WINDOWS开关
    
    class MA
    {
    public:
    	//把收到的消息(玩家命令)放入到一个队列的线程
    	void inMsgRecvQueue()
    	{
    		for (int i = 0; i < 100000; ++i)
    		{
    			cout << "inMsgRecvQueue()执行,插入一个元素: " << i << endl;
    
    #ifdef __WINDOWSJQ_ 
    			EnterCriticalSection(&my_winsec);  //进入临界区(加锁)
    			msgRecvQueue.push_back(i);  //假设这个数字i就是我收到的命令,直接弄到消息队列中
    			LeaveCriticalSection(&my_winsec);  //离开临界区(解锁)
    #else
    			my_mutex.lock();
    			msgRecvQueue.push_back(i);  //假设这个数字i就是我收到的命令,直接弄到消息队列中
    			my_mutex.unlock();
    #endif
    		}
    	}
    
    	bool outMsgLUProc(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;
    	}
    
    	//把数据从消息队列中取出的线程
    	void outMsgRecvQueue()
    	{
    		int command = 0;
    		for (int i = 0; i < 100000; ++i)
    		{
    			bool result = outMsgLUProc(command);
    			if (result == true)
    			{
    				cout << "outMsgRecvQueue()执行,取出一个元素" << endl;
    				//这里就考虑处理数据...
    				//...
    			}
    			else
    			{
    				cout << "outMsgRecvQueue()执行,但目前消息队列中为空 " << i << endl;
    			}
    		}
    		cout << "end" << endl;
    	}
    
    	MA()
    	{
    #ifdef __WINDOWSJQ_ 
    		InitializeCriticalSection(&my_winsec);  //用临界区之前要先初始化
    #endif
    	}
    
    private:
    	list<int> msgRecvQueue;  //容器(消息队列),专门用于代表玩家发送的命令
    	std::mutex my_mutex;  //创建了一个互斥量
    #ifdef __WINDOWSJQ_  //解开开关
    	CRITICAL_SECTION my_winsec;  //windwos中的临界区,非常类似于c++11中的mutex
    #endif
    };
    
    int main()
    {
    	MA myobj;
    	std::thread myOutMsgObj(&MA::outMsgRecvQueue, &myobj);  //第二个参数是引用,才能保证线程里用的是同一个对象
    	std::thread myInMsgObj(&MA::inMsgRecvQueue, &myobj);
    	myOutMsgObj.join();
    	myInMsgObj.join();
    	cout << "main主函数执行结束" << endl;  //最后执行这句,整个进程退出
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105

    二:多次进入临界区试验

    在同一线程中(不同线程就会卡住等待)中,windows中的“相同临界区变量”代表的临界区的进入EnterCriticalSection()可以被多次调用。但是调用几次EnterCriticalSection(),就得调用几次LeaveCriticalSection()。

    #define __WINDOWSJQ_  //定义一个开关,WINDOWS开关
    
    class MA
    {
    public:
    	//把收到的消息(玩家命令)放入到一个队列的线程
    	void inMsgRecvQueue()
    	{
    		for (int i = 0; i < 100000; ++i)
    		{
    			cout << "inMsgRecvQueue()执行,插入一个元素: " << i << endl;
    
    #ifdef __WINDOWSJQ_ 
    			EnterCriticalSection(&my_winsec);  //进入临界区(加锁)
    			EnterCriticalSection(&my_winsec);  //两次进入临界区(加锁)
    			msgRecvQueue.push_back(i);  //假设这个数字i就是我收到的命令,直接弄到消息队列中
    			LeaveCriticalSection(&my_winsec);  //离开临界区(解锁)
    			LeaveCriticalSection(&my_winsec);  //两次进入必须两次离开
    #else
    			my_mutex.lock();
    			msgRecvQueue.push_back(i);  //假设这个数字i就是我收到的命令,直接弄到消息队列中
    			my_mutex.unlock();
    #endif
    		}
    	}
    
    	bool outMsgLUProc(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;
    	}
    
    	//把数据从消息队列中取出的线程
    	void outMsgRecvQueue()
    	{
    		int command = 0;
    		for (int i = 0; i < 100000; ++i)
    		{
    			bool result = outMsgLUProc(command);
    			if (result == true)
    			{
    				cout << "outMsgRecvQueue()执行,取出一个元素" << endl;
    				//这里就考虑处理数据...
    				//...
    			}
    			else
    			{
    				cout << "outMsgRecvQueue()执行,但目前消息队列中为空 " << i << endl;
    			}
    		}
    		cout << "end" << endl;
    	}
    
    	MA()
    	{
    #ifdef __WINDOWSJQ_ 
    		InitializeCriticalSection(&my_winsec);  //用临界区之前要先初始化
    #endif
    	}
    
    private:
    	list<int> msgRecvQueue;  //容器(消息队列),专门用于代表玩家发送的命令
    	std::mutex my_mutex;  //创建了一个互斥量
    #ifdef __WINDOWSJQ_  //解开开关
    	CRITICAL_SECTION my_winsec;  //windwos中的临界区,非常类似于c++11中的mutex
    #endif
    };
    
    int main()
    {
    	MA myobj;
    	std::thread myOutMsgObj(&MA::outMsgRecvQueue, &myobj);  //第二个参数是引用,才能保证线程里用的是同一个对象
    	std::thread myInMsgObj(&MA::inMsgRecvQueue, &myobj);
    	myOutMsgObj.join();
    	myInMsgObj.join();
    	cout << "main主函数执行结束" << endl;  //最后执行这句,整个进程退出
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100

    在c++11中加两次锁就会报错,不允许同一个线程中lock同一个互斥量多次,否则报异常。

    my_mutex.lock();
    my_mutex.lock();  //报异常,和windows有区别
    msgRecvQueue.push_back(i);     
    my_mutex.unlock();
    my_mutex.unlock();
    
    • 1
    • 2
    • 3
    • 4
    • 5

    三:自动析构技术

    std::lock_guardstd::mutex
    std::lock_guardstd::mutex sbguard(my_mutex);
    std::lock_guardstd::mutex sbguard1(my_mutex);
    lock_guard也不能调用两次

    using namespace std;
    
    //#define __WINDOWSJQ_  //定义一个开关,WINDOWS开关
    
    class MA
    {
    public:
    	//把收到的消息(玩家命令)放入到一个队列的线程
    	void inMsgRecvQueue()
    	{
    		for (int i = 0; i < 100000; ++i)
    		{
    			cout << "inMsgRecvQueue()执行,插入一个元素: " << i << endl;
    
    #ifdef __WINDOWSJQ_ 
    			EnterCriticalSection(&my_winsec);  //进入临界区(加锁)
    			EnterCriticalSection(&my_winsec);  //两次进入临界区(加锁)
    			msgRecvQueue.push_back(i);  //假设这个数字i就是我收到的命令,直接弄到消息队列中
    			LeaveCriticalSection(&my_winsec);  //离开临界区(解锁)
    			LeaveCriticalSection(&my_winsec);  //两次进入必须两次离开
    #else
    			std::lock_guard<std::mutex> sbguard(my_mutex);
    			std::lock_guard<std::mutex> sbguard1(my_mutex);
    			msgRecvQueue.push_back(i);  //假设这个数字i就是我收到的命令,直接弄到消息队列中
    #endif
    		}
    	}
    
    	bool outMsgLUProc(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;
    	}
    
    	//把数据从消息队列中取出的线程
    	void outMsgRecvQueue()
    	{
    		int command = 0;
    		for (int i = 0; i < 100000; ++i)
    		{
    			bool result = outMsgLUProc(command);
    			if (result == true)
    			{
    				cout << "outMsgRecvQueue()执行,取出一个元素" << endl;
    				//这里就考虑处理数据...
    				//...
    			}
    			else
    			{
    				cout << "outMsgRecvQueue()执行,但目前消息队列中为空 " << i << endl;
    			}
    		}
    		cout << "end" << endl;
    	}
    
    	MA()
    	{
    #ifdef __WINDOWSJQ_ 
    		InitializeCriticalSection(&my_winsec);  //用临界区之前要先初始化
    #endif
    	}
    
    private:
    	list<int> msgRecvQueue;  //容器(消息队列),专门用于代表玩家发送的命令
    	std::mutex my_mutex;  //创建了一个互斥量
    #ifdef __WINDOWSJQ_  //解开开关
    	CRITICAL_SECTION my_winsec;  //windwos中的临界区,非常类似于c++11中的mutex
    #endif
    };
    
    int main()
    {
    	MA myobj;
    	std::thread myOutMsgObj(&MA::outMsgRecvQueue, &myobj);  //第二个参数是引用,才能保证线程里用的是同一个对象
    	std::thread myInMsgObj(&MA::inMsgRecvQueue, &myobj);
    	myOutMsgObj.join();
    	myInMsgObj.join();
    	cout << "main主函数执行结束" << endl;  //最后执行这句,整个进程退出
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102

    自动释放windows下的临界区,防止忘记LeaveCriticalSection()导致死锁情况的发生,类似于c++11中的std::lock_guardstd::mutex功能。

    #define __WINDOWSJQ_  //定义一个开关,WINDOWS开关
    
    //本类用于自动释放windows下的临界区,防止忘记LeaveCriticalSection()导致死锁情况的发生,类似于c++11中的std::lock_guard<std::mutex>功能
    class CWinLock  //叫RAII类(Resourse Acquisition is initialization)中文“资源获取及初始化”。容器、智能指针这种类,都属于RAII类
    {     
    public:
    	CWinLock(CRITICAL_SECTION* pCripem)  //构造函数
    	{   
    		m_pCritical = pCripem;
    		EnterCriticalSection(m_pCritical);
    	}
    	~CWinLock()  //析构函数
    	{   
    		LeaveCriticalSection(m_pCritical);
    	}
    private:
    	CRITICAL_SECTION* m_pCritical;
    };
    
    class MA
    {
    public:
    	//把收到的消息(玩家命令)放入到一个队列的线程
    	void inMsgRecvQueue()
    	{
    		for (int i = 0; i < 100000; ++i)
    		{
    			cout << "inMsgRecvQueue()执行,插入一个元素: " << i << endl;
    
    #ifdef __WINDOWSJQ_ 
    			CWinLock wlock(&my_winsec);  //wlock,wlock2都属于RAII类
    			CWinLock wlock2(&my_winsec);  //wlock,wlock2都属于RAII类
    			msgRecvQueue.push_back(i);  //假设这个数字i就是我收到的命令,直接弄到消息队列中
    #else
    			std::lock_guard<std::mutex> sbguard(my_mutex);
    			msgRecvQueue.push_back(i);  //假设这个数字i就是我收到的命令,直接弄到消息队列中
    #endif
    		}
    	}
    
    	bool outMsgLUProc(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;
    	}
    
    	//把数据从消息队列中取出的线程
    	void outMsgRecvQueue()
    	{
    		int command = 0;
    		for (int i = 0; i < 100000; ++i)
    		{
    			bool result = outMsgLUProc(command);
    			if (result == true)
    			{
    				cout << "outMsgRecvQueue()执行,取出一个元素" << endl;
    				//这里就考虑处理数据...
    				//...
    			}
    			else
    			{
    				cout << "outMsgRecvQueue()执行,但目前消息队列中为空 " << i << endl;
    			}
    		}
    		cout << "end" << endl;
    	}
    
    	MA()
    	{
    #ifdef __WINDOWSJQ_ 
    		InitializeCriticalSection(&my_winsec);  //用临界区之前要先初始化
    #endif
    	}
    
    private:
    	list<int> msgRecvQueue;  //容器(消息队列),专门用于代表玩家发送的命令
    	std::mutex my_mutex;  //创建了一个互斥量
    #ifdef __WINDOWSJQ_  //解开开关
    	CRITICAL_SECTION my_winsec;  //windwos中的临界区,非常类似于c++11中的mutex
    #endif
    };
    
    int main()
    {
    	MA myobj;
    	std::thread myOutMsgObj(&MA::outMsgRecvQueue, &myobj);  //第二个参数是引用,才能保证线程里用的是同一个对象
    	std::thread myInMsgObj(&MA::inMsgRecvQueue, &myobj);
    	myOutMsgObj.join();
    	myInMsgObj.join();
    	cout << "main主函数执行结束" << endl;  //最后执行这句,整个进程退出
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114

    四:recursive_mutex递归的独占互斥量

    std::mutex:独占互斥量,自己lock时,别人就lock不了。
    recursive_mutex:递归的独占互斥量。允许同一个线程,同一个互斥量多次被lock()。效率上比mutex要差一些。

    std::lock_guardstd::recursive_mutex sbguard(my_mutex);
    testfun1();

    如果出现recursive_mutex,就要考虑代码的效率是不是有问题,最好能不使用。递归次数有限制,递归太多可能报异常。

    //#define __WINDOWSJQ_  //定义一个开关,WINDOWS开关
    
    //本类用于自动释放windows下的临界区,防止忘记LeaveCriticalSection()导致死锁情况的发生,类似于c++11中的std::lock_guard<std::mutex>功能
    class CWinLock  //叫RAII类(Resourse Acquisition is initialization)中文“资源获取及初始化”。容器、智能指针这种类,都属于RAII类
    {
    public:
    	CWinLock(CRITICAL_SECTION* pCripem)  //构造函数
    	{
    		m_pCritical = pCripem;
    		EnterCriticalSection(m_pCritical);
    	}
    	~CWinLock()  //析构函数
    	{
    		LeaveCriticalSection(m_pCritical);
    	}
    private:
    	CRITICAL_SECTION* m_pCritical;
    };
    
    class MA
    {
    public:
    	//把收到的消息(玩家命令)放入到一个队列的线程
    	void inMsgRecvQueue()
    	{
    		for (int i = 0; i < 100000; ++i)
    		{
    			cout << "inMsgRecvQueue()执行,插入一个元素: " << i << endl;
    
    #ifdef __WINDOWSJQ_ 
    			CWinLock wlock(&my_winsec);  //wlock,wlock2都属于RAII类
    			CWinLock wlock2(&my_winsec);  //wlock,wlock2都属于RAII类
    			msgRecvQueue.push_back(i);  //假设这个数字i就是我收到的命令,直接弄到消息队列中
    #else
    			std::lock_guard<std::recursive_mutex> sbguard(my_mutex);
    			testfun1();  //加了三次锁,报异常(只要lock超过1次就报异常)
    			msgRecvQueue.push_back(i);  //假设这个数字i就是我收到的命令,直接弄到消息队列中
    #endif
    		}
    	}
    
    	bool outMsgLUProc(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;
    	}
    
    	//把数据从消息队列中取出的线程
    	void outMsgRecvQueue()
    	{
    		int command = 0;
    		for (int i = 0; i < 100000; ++i)
    		{
    			bool result = outMsgLUProc(command);
    			if (result == true)
    			{
    				cout << "outMsgRecvQueue()执行,取出一个元素" << endl;
    				//这里就考虑处理数据...
    				//...
    			}
    			else
    			{
    				cout << "outMsgRecvQueue()执行,但目前消息队列中为空 " << i << endl;
    			}
    		}
    		cout << "end" << endl;
    	}
    
    	MA()
    	{
    #ifdef __WINDOWSJQ_ 
    		InitializeCriticalSection(&my_winsec);  //用临界区之前要先初始化
    #endif
    	}
    
    	void testfun1()
    	{
    		std::lock_guard<std::recursive_mutex> sbguard(my_mutex);
    		//干一些事情
    		testfun2();  //奔溃
    	}
    	void testfun2()
    	{
    		std::lock_guard<std::recursive_mutex> sbguard(my_mutex);
    		//干另一些事情
    	}
    
    private:
    	list<int> msgRecvQueue;  //容器(消息队列),专门用于代表玩家发送的命令
    	//std::mutex my_mutex;  //创建了一个互斥量
    	std::recursive_mutex my_mutex;  //递归式的独占互斥量
    #ifdef __WINDOWSJQ_  //解开开关
    	CRITICAL_SECTION my_winsec;  //windwos中的临界区,非常类似于c++11中的mutex
    #endif
    };
    
    int main()
    {
    	MA myobj;
    	std::thread myOutMsgObj(&MA::outMsgRecvQueue, &myobj);  //第二个参数是引用,才能保证线程里用的是同一个对象
    	std::thread myInMsgObj(&MA::inMsgRecvQueue, &myobj);
    	myOutMsgObj.join();
    	myInMsgObj.join();
    	cout << "main主函数执行结束" << endl;  //最后执行这句,整个进程退出
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128

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

    <1>std::timed_mutex :带超时功能的独占互斥量

    try_lock_for():参数是等待一段时间。如果我拿到了锁,或者等待超过时间没拿到锁头,就走下来;

    //#define __WINDOWSJQ_  //定义一个开关,WINDOWS开关
    
    //本类用于自动释放windows下的临界区,防止忘记LeaveCriticalSection()导致死锁情况的发生,类似于c++11中的std::lock_guard<std::mutex>功能
    class CWinLock  //叫RAII类(Resourse Acquisition is initialization)中文“资源获取及初始化”。容器、智能指针这种类,都属于RAII类
    {
    public:
    	CWinLock(CRITICAL_SECTION* pCripem)  //构造函数
    	{
    		m_pCritical = pCripem;
    		EnterCriticalSection(m_pCritical);
    	}
    	~CWinLock()  //析构函数
    	{
    		LeaveCriticalSection(m_pCritical);
    	}
    private:
    	CRITICAL_SECTION* m_pCritical;
    };
    
    class MA
    {
    public:
    	//把收到的消息(玩家命令)放入到一个队列的线程
    	void inMsgRecvQueue()
    	{
    		for (int i = 0; i < 100000; ++i)
    		{
    			cout << "inMsgRecvQueue()执行,插入一个元素: " << i << endl;
    
    #ifdef __WINDOWSJQ_ 
    			CWinLock wlock(&my_winsec);  //wlock,wlock2都属于RAII类
    			CWinLock wlock2(&my_winsec);  //wlock,wlock2都属于RAII类
    			msgRecvQueue.push_back(i);  //假设这个数字i就是我收到的命令,直接弄到消息队列中
    #else
    			std::chrono::milliseconds timeout(100);  //100毫秒
    			if (my_mutex.try_lock_for(timeout))  //等待一百毫秒尝试获取锁头 
    			{
    				//在这100毫秒之内拿到了锁
    				msgRecvQueue.push_back(i);
    				my_mutex.unlock();  //用完了要解锁;
    			}
    			else
    			{
    				//这次没拿到锁头
    				std::chrono::milliseconds sleeptime(100);
    				std::this_thread::sleep_for(sleeptime);
    			}
    			msgRecvQueue.push_back(i);  //假设这个数字i就是我收到的命令,直接弄到消息队列中
    #endif
    		}
    	}
    
    	bool outMsgLUProc(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();
    		std::chrono::milliseconds sleeptime(1000000);
    		std::this_thread::sleep_for(sleeptime);
    		if (!msgRecvQueue.empty())
    		{
    			//消息不为空
    			command = msgRecvQueue.front();  //返回第一个元素,但不检查元素是否存在;
    			msgRecvQueue.pop_front();  //移除第一个元素,但不返回
    			my_mutex.unlock();
    			return true;
    		}
    		my_mutex.unlock();
    #endif
    		return false;
    	}
    
    	//把数据从消息队列中取出的线程
    	void outMsgRecvQueue()
    	{
    		int command = 0;
    		for (int i = 0; i < 100000; ++i)
    		{
    			bool result = outMsgLUProc(command);
    			if (result == true)
    			{
    				cout << "outMsgRecvQueue()执行,取出一个元素" << endl;
    				//这里就考虑处理数据...
    				//...
    			}
    			else
    			{
    				cout << "outMsgRecvQueue()执行,但目前消息队列中为空 " << i << endl;
    			}
    		}
    		cout << "end" << endl;
    	}
    
    	MA()
    	{
    #ifdef __WINDOWSJQ_ 
    		InitializeCriticalSection(&my_winsec);  //用临界区之前要先初始化
    #endif
    }
    
    private:
    	list<int> msgRecvQueue;  //容器(消息队列),专门用于代表玩家发送的命令
    	//std::mutex my_mutex;  //创建了一个互斥量
    	std::timed_mutex my_mutex;  //带超时功能的独占互斥量
    #ifdef __WINDOWSJQ_  //解开开关
    	CRITICAL_SECTION my_winsec;  //windwos中的临界区,非常类似于c++11中的mutex
    #endif
    };
    
    int main()
    {
    	MA myobj;
    	std::thread myOutMsgObj(&MA::outMsgRecvQueue, &myobj);  //第二个参数是引用,才能保证线程里用的是同一个对象
    	std::thread myInMsgObj(&MA::inMsgRecvQueue, &myobj);
    	myOutMsgObj.join();
    	myInMsgObj.join();
    	cout << "main主函数执行结束" << endl;  //最后执行这句,整个进程退出
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129

    try_lock_until():参数是一个未来的时间点,在这个未来时间没到的时间段内。如果拿到了锁,那么就走下来;如果时间到了,没拿到锁,程序流程也走下来。

    	void inMsgRecvQueue()
    	{
    		for (int i = 0; i < 100000; ++i)
    		{
    			cout << "inMsgRecvQueue()执行,插入一个元素: " << i << endl;
    
    #ifdef __WINDOWSJQ_ 
    			CWinLock wlock(&my_winsec);  //wlock,wlock2都属于RAII类
    			CWinLock wlock2(&my_winsec);  //wlock,wlock2都属于RAII类
    			msgRecvQueue.push_back(i);  //假设这个数字i就是我收到的命令,直接弄到消息队列中
    #else
    			std::chrono::milliseconds timeout(100);  //100毫秒
    			if (my_mutex.try_lock_until(chrono::steady_clock::now() + timeout))
    			{
    				//在这100毫秒之内拿到了锁
    				msgRecvQueue.push_back(i);
    				my_mutex.unlock();  //用完了要解锁;
    			}
    			else
    			{
    				//这次没拿到锁头
    				std::chrono::milliseconds sleeptime(100);
    				std::this_thread::sleep_for(sleeptime);
    			}
    			msgRecvQueue.push_back(i);  //假设这个数字i就是我收到的命令,直接弄到消息队列中
    #endif
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    <2>std::recursive_timed_mutex:带超时功能的递归独占互斥量(允许同一个线程多次获取这个互斥量)。

  • 相关阅读:
    快速解决 adb server version doesn‘t match this client
    常用的特征分析和特征工程
    JS基础
    【牛客刷题5】:格式化输出3(Python)
    行情分析——加密货币市场大盘走势(11.7)
    通过python 获取当前局域网内存在的IP和MAC
    00后会不会改变软件测试行业现状?
    1999-2018年地级市经济增长数据
    力扣练习——70 串联所有单词的子串
    并行多核体系结构基础 Yan Solihin 第3章 共享存储并行编程 摘录
  • 原文地址:https://blog.csdn.net/zzyzxb/article/details/125453135