#include "pch.h"
#include <iostream>
#include <thread>
#include <vector>
#include <list>
#include <mutex>
#include <windows.h>
using namespace std;
//开关
//# define __WINDOWSJQ_
//本类用于自动释放windows下的临界区,防止忘记LeaveCriticalSection导致死锁情况的发生,类似于C++11中的lock_guard
class CWinlock //叫RAII类(Resource Asquisition is initialization ),该类生成的对象叫RAII对象 //资源获取即初始化
{
public:
CWinlock(CRITICAL_SECTION *pCritmp) //构造函数进入临界区
{
m_pCritical = pCritmp;
EnterCriticalSection(m_pCritical);
}
~CWinlock() //析构函数离开临界区
{
LeaveCriticalSection(m_pCritical);
}
private:
CRITICAL_SECTION * m_pCritical;
};
//具备mutex的类
class A_Mutex {
public:
A_Mutex() //在构造函数将临界区初始化
{
#ifdef __WINDOWSJQ_
InitializeCriticalSection(&my_winsec); //用临界区之前要先初始化
#endif // __WINDOWSJQ_
}
void inMsgRecvQueue() //把收到的消息(玩家命令)到一个队列的线程。100000次便于观察
{
for (int i = 0; i < 100000; i++)
{
cout << "inMsgRecvQueue()执行,插入一个元素" << i << endl;
#ifdef __WINDOWSJQ_
//EnterCriticalSection(&my_winsec); //进入临界区(加锁)
//EnterCriticalSection(&my_winsec); //同一线程中可以多次进入临界区
CWinlock wlock(&my_winsec); //自动离开临界区
CWinlock wlock2(&my_winsec); //也可以多次进入
msgRecvQueue.push_back(i);
//LeaveCriticalSection(&my_winsec); //离开临界区(解锁)
//LeaveCriticalSection(&my_winsec); //进入几次就需要离开几次
#else
//...处理代码
//my_mutex.lock();
//my_mutex.lock(); //同一线程不可以对同一个互斥量lock两次,除非是recursive的互斥量
//msgRecvQueue.push_back(i); //假设这个数字i就是我收到的命令,直接送到消息队列里去
//my_mutex.unlock();
//my_mutex.unlock();
//带超时功能的独占互斥量
chrono::milliseconds timeout(100); //100毫秒
//if (my_mutex.try_lock_for(timeout)) //try_lock_for,在这100毫秒内拿到了锁
if (my_mutex.try_lock_until(chrono::steady_clock::now()+timeout)) //try_lock_until,在未来的时间点到来前拿到了锁(这里设置的是当前时间后的100毫秒)
{
msgRecvQueue.push_back(i);
my_mutex.unlock(); //用完后解锁
}
else
{
//暂未拿到锁
chrono::milliseconds sleeptime(100); //100毫秒
this_thread::sleep_for(sleeptime);
}
#endif // __WINDOWSJQ_
//....处理其他代码
}
return;
}
bool outMsgLULProc(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;
}
//注意这里也有一个unlock(另外一个出口也需要unlock)
my_mutex.unlock();
#endif
return false;
}
void outMsgRecvQueue()//把数据从消息队列中取出的线程
{
int command = 0;
for (int i = 0; i < 100000; i++)
{
if (outMsgLULProc(command) == true)
{
cout << "outMsgRecvQueue()执行" << i << endl;
//可以对command进行数据处理
}
else
{
//消息队列为空
cout << "outMsgRecvQueue()执行,但目前消息队列为空" << i << endl;
}
}
cout << "end" << endl;
return;
}
private:
list <int> msgRecvQueue; //容器(消息队列),专门用于代表玩家发送过来的命令
//mutex my_mutex; //创建了一个独占互斥量(一个锁)
//recursive_mutex my_mutex; //创建了一个递归的独占互斥量
timed_mutex my_mutex; //带超时功能的独占互斥量
#ifdef __WINDOWSJQ_
CRITICAL_SECTION my_winsec; //windows中的临界区,类似于C++11的mutex
#endif // __WINDOWSJQ_
};
int main()
{
//一:windows临界区
//二:多次进入临界区试验
//在同一线程中(不同的线程中则需等待),windows中的“相同临界区变量”代表的临界区的进入可以被多次调用。N次调用就需要N次释放。
//而C++中,同一线程不可以对同一个互斥量lock两次
//三:自动析构技术
//四:recursive_mutex 递归的独占互斥量
//允许同一线程,对同一互斥量多次lock,效率上比mutex低,消耗更大
//尽量优化代码,减少上锁次数
//五:带超时的互斥量timed_mutex和recursive_timed_mutex
//timed_mutex:带超时功能的独占互斥量。
//try_lock_for():等待一段时间。如果拿到了锁或者等待超过时间(未拿到锁),流程走下来
//try_lock_until():等待直至一个未来的时间点。如果提前拿到了锁或者直至未来的时间还未拿到锁,流程走下来
//recursive_timed_mutex:带超时功能的递归独占互斥量
A_Mutex myobja;
thread myOutnMsgObj(&A_Mutex::outMsgRecvQueue,&myobja); //第二参数是引用(就无需拷贝),才能保证线程里用的是同一对象
thread myInMsgobj(&A_Mutex::inMsgRecvQueue, &myobja);
myOutnMsgObj.join();
myInMsgobj.join();
cout << "主线程执行完毕" << endl; //执行完这句,主线程退出
return 0;
}