文章目录
1、Windows临界区
1.1 创建Windows临界区
CRITICAL_SECTION 临界区名
CRITICAL_SECTION my_winsec; //Windows中的临界区,非常类似于c++11中的mutex
1.2 临界区初始化
InitializeCriticalSection(临界区名)
InitializeCriticalSection(&my_winsec); //用临界区之前要先初始化
1.3 进入临界区
EnterCriticalSection(临界区名)
EnterCriticalSection(&my_winsec); //进入Windows临界区,和mutex加锁一个道理
1.4 离开临界区
LeaveCriticalSection(临界区名)
LeaveCriticalSection(&my_winsec); //离开Windows临界区,和mutex解锁一个道理
1.5 示例代码
#include <iostream>
#include <thread>
#include <vector>
#include <list>
#include <mutex>
#include <Windows.h>
using namespace std;
#define __WINDOWSJQ_
class A
{
public:
//把收到的消息(玩家命令)入到一个队列的线程
void inMsgRecvQueue()
{
for (int i = 0; i < 100000; i++)
{
cout << "inMsgRecvQueue()执行,插入一个元素:" << i << endl;
#ifdef __WINDOWSJQ_
EnterCriticalSection(&my_winsec); //进入Windows临界区,和mutex加锁一个道理
msgRecvQueue.push_back(i);
LeaveCriticalSection(&my_winsec); //离开Windows临界区,和mutex解锁一个道理
#else
my_mutex.lock();
msgRecvQueue.push_back(i);
my_mutex.unlock();
#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;
}
my_mutex.unlock();
#endif // __WINDOWSJQ_
return false;
}
//把数据从消息队列中取出的线程
void outMsgRecvQueue()
{
int command = 0;
for (int i = 0; i < 100000; i++)
{
bool result = outMsgLULProc(command);
if (result == true)
{
cout << "outMsgRecvQueue()执行,取出一个元素" << command << endl;
//接下来就考虑处理数据......
}
else
{
//消息队列为空
cout << "outMsgRecvQueue()执行,但目前消息队列中为空" << i << endl;
}
}
}
A()
{
#ifdef __WINDOWSJQ_
InitializeCriticalSection(&my_winsec); //用临界区之前要先初始化
#endif // __WINDOWSJQ_
}
private:
list<int> msgRecvQueue; //容器,专门用于代表玩家给咱们发送过来的命令
mutex my_mutex; //创建了一个互斥量
#ifdef __WINDOWSJQ_
CRITICAL_SECTION my_winsec; //Windows中的临界区,非常类似于c++11中的mutex
#endif // __WINDOWSJQ_
};
int main()
{
A myobja;
thread myOutnMsgObj(&A::outMsgRecvQueue, &myobja);
thread myInMsgObj(&A::inMsgRecvQueue, &myobja);
myInMsgObj.join();
myOutnMsgObj.join();
cout << "I live China!" << endl; //最后执行这句,整个进程退出
system("pause");
return 0;
}
2、多次进入临界区试验
- 在同一个线程中,Windows中的相同临界区变量代表的临界区可以被多次进入(
EnterCriticalSection
),但是调用了几次EnterCriticalSection
就得调用几次LeaveCriticalSection
。 - 但是c++11中的同一个
mutex
是不支持在同一个线程中多次lock()
和unlock()
的,否则报异常。
EnterCriticalSection(&my_winsec); //进入Windows临界区,和mutex加锁一个道理
EnterCriticalSection(&my_winsec);
msgRecvQueue.push_back(i);
LeaveCriticalSection(&my_winsec); //离开Windows临界区,和mutex解锁一个道理
LeaveCriticalSection(&my_winsec);
3、自动析构技术
- 编写一个类实现自动进入临界区和离开临界区,和C++11的
lock_guard
一样。
示例代码:
类代码:
class CWinLock //叫RAII类(Resource Acquisition is initialization)中文:“资源获取即初始化”
{
public:
CWinLock(CRITICAL_SECTION* pCritmp) //构造函数
{
m_pCritical = pCritmp;
EnterCriticalSection(m_pCritical);
}
~CWinLock() //析构函数
{
LeaveCriticalSection(m_pCritical);
}
private:
CRITICAL_SECTION* m_pCritical;
};
程序整体代码:
#include <iostream>
#include <thread>
#include <vector>
#include <list>
#include <mutex>
#include <Windows.h>
using namespace std;
#define __WINDOWSJQ_
class CWinLock //叫RAII类(Resource Acquisition is initialization)中文:“资源获取即初始化”
{
public:
CWinLock(CRITICAL_SECTION* pCritmp) //构造函数
{
m_pCritical = pCritmp;
EnterCriticalSection(m_pCritical);
}
~CWinLock() //析构函数
{
LeaveCriticalSection(m_pCritical);
}
private:
CRITICAL_SECTION* m_pCritical;
};
class A
{
public:
//把收到的消息(玩家命令)入到一个队列的线程
void inMsgRecvQueue()
{
for (int i = 0; i < 100000; i++)
{
cout << "inMsgRecvQueue()执行,插入一个元素:" << i << endl;
#ifdef __WINDOWSJQ_
//EnterCriticalSection(&my_winsec); //进入Windows临界区,和mutex加锁一个道理
CWinLock wlock(&my_winsec);
CWinLock wlock2(&my_winsec); //可以多次调用
msgRecvQueue.push_back(i);
//LeaveCriticalSection(&my_winsec); //离开Windows临界区,和mutex解锁一个道理
#else
my_mutex.lock();
msgRecvQueue.push_back(i);
my_mutex.unlock();
#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;
}
my_mutex.unlock();
#endif // __WINDOWSJQ_
return false;
}
//把数据从消息队列中取出的线程
void outMsgRecvQueue()
{
int command = 0;
for (int i = 0; i < 100000; i++)
{
bool result = outMsgLULProc(command);
if (result == true)
{
cout << "outMsgRecvQueue()执行,取出一个元素" << command << endl;
//接下来就考虑处理数据......
}
else
{
//消息队列为空
cout << "outMsgRecvQueue()执行,但目前消息队列中为空" << i << endl;
}
}
}
A()
{
#ifdef __WINDOWSJQ_
InitializeCriticalSection(&my_winsec); //用临界区之前要先初始化
#endif // __WINDOWSJQ_
}
private:
list<int> msgRecvQueue; //容器,专门用于代表玩家给咱们发送过来的命令
mutex my_mutex; //创建了一个互斥量
#ifdef __WINDOWSJQ_
CRITICAL_SECTION my_winsec; //Windows中的临界区,非常类似于c++11中的mutex
#endif // __WINDOWSJQ_
};
int main()
{
A myobja;
thread myOutnMsgObj(&A::outMsgRecvQueue, &myobja);
thread myInMsgObj(&A::inMsgRecvQueue, &myobja);
myInMsgObj.join();
myOutnMsgObj.join();
cout << "I live China!" << endl; //最后执行这句,整个进程退出
system("pause");
return 0;
}
4、recursive_mutex
recursive_mutex
:递归的独占互斥量,允许同一个线程,同一个互斥量被多次lock()
。recursive_mutex
也有lock()
和unlcok()
。- 递归次数有限,递归太多次可能报异常,一般情况下,够用。
- 使用
recursive_mutex
时,要考虑代码是否有优化空间。
示例代码:
#include <iostream>
#include <thread>
#include <vector>
#include <list>
#include <mutex>
#include <Windows.h>
using namespace std;
class A
{
public:
//把收到的消息(玩家命令)入到一个队列的线程
void inMsgRecvQueue()
{
for (int i = 0; i < 100000; i++)
{
std::lock_guard<std::recursive_mutex> sbguard(my_mutex);
testfunc1();
msgRecvQueue.push_back(i);
cout << "inMsgRecvQueue()执行,插入一个元素:" << i << endl;
}
return;
}
bool outMsgLULProc(int& command)
{
std::lock_guard<std::recursive_mutex> sbguard(my_mutex);
if (!msgRecvQueue.empty())
{
//消息不为空
command = msgRecvQueue.front(); //返回第一个元素,但不检查元素是否存在
msgRecvQueue.pop_front(); //移除第一个元素,但不返回
return true;
}
return false;
}
//把数据从消息队列中取出的线程
void outMsgRecvQueue()
{
int command = 0;
for (int i = 0; i < 100000; i++)
{
bool result = outMsgLULProc(command);
if (result == true)
{
cout << "outMsgRecvQueue()执行,取出一个元素" << command << endl;
//接下来就考虑处理数据......
}
else
{
//消息队列为空
cout << "outMsgRecvQueue()执行,但目前消息队列中为空" << i << endl;
}
}
}
void testfunc1()
{
std::lock_guard<std::recursive_mutex> sbguard(my_mutex);
//......
testfunc2();
}
void testfunc2()
{
std::lock_guard<std::recursive_mutex> sbguard(my_mutex);
//......
}
private:
list<int> msgRecvQueue; //容器,专门用于代表玩家给咱们发送过来的命令
std::recursive_mutex my_mutex; //创建了一个递归独占互斥量
};
int main()
{
A myobja;
thread myOutnMsgObj(&A::outMsgRecvQueue, &myobja);
thread myInMsgObj(&A::inMsgRecvQueue, &myobja);
myInMsgObj.join();
myOutnMsgObj.join();
cout << "I live China!" << endl; //最后执行这句,整个进程退出
system("pause");
return 0;
}
5、带超时的互斥量std::timed_mutex和std::recursive_timed_mutex
5.1 std::timed_mutex
- 带超时功能的独占互斥量
try_lock_for()
-
等待一段时间。
-
参数是一段时间。
-
如果在等待时间内拿到了锁,或者超过等待时间没拿到锁,就会继续走下去。
-
示例代码:
#include <iostream> #include <thread> #include <vector> #include <list> #include <mutex> #include <Windows.h> using namespace std; class A { public: //把收到的消息(玩家命令)入到一个队列的线程 void inMsgRecvQueue() { for (int i = 0; i < 100000; i++) { msgRecvQueue.push_back(i); std::chrono::milliseconds timeout(100); //100ms if (my_mutex.try_lock_for(timeout)) //等待100ms来尝试获取锁 { //再100ms内拿到了锁 msgRecvQueue.push_back(i); cout << "inMsgRecvQueue()执行,插入一个元素:" << i << endl; my_mutex.unlock(); //用完了要解锁 } else { //这次没拿到锁头,休息一下,休息完再尝试 std::chrono::milliseconds sleeptime(100); std::this_thread::sleep_for(sleeptime); } } return; } bool outMsgLULProc(int& command) { my_mutex.lock(); std::chrono::milliseconds cutTime(100000); std::this_thread::sleep_for(cutTime); 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 = outMsgLULProc(command); if (result == true) { cout << "outMsgRecvQueue()执行,取出一个元素" << command << endl; //接下来就考虑处理数据...... } else { //消息队列为空 cout << "outMsgRecvQueue()执行,但目前消息队列中为空" << i << endl; } } } private: list<int> msgRecvQueue; //容器,专门用于代表玩家给咱们发送过来的命令 std::timed_mutex my_mutex; //创建了一个带超时功能的独占互斥量 }; int main() { A myobja; thread myOutnMsgObj(&A::outMsgRecvQueue, &myobja); thread myInMsgObj(&A::inMsgRecvQueue, &myobja); myInMsgObj.join(); myOutnMsgObj.join(); cout << "I live China!" << endl; //最后执行这句,整个进程退出 system("pause"); return 0; }
try_lock_until()
-
参数是一个未来的时间点,在还没有到未来时间点的这段时间内,如果拿到了锁头,流程就走下来,如果时间到了没拿到锁,流程也会走下来。
#include <iostream> #include <thread> #include <vector> #include <list> #include <mutex> #include <Windows.h> using namespace std; class A { public: //把收到的消息(玩家命令)入到一个队列的线程 void inMsgRecvQueue() { for (int i = 0; i < 100000; i++) { msgRecvQueue.push_back(i); std::chrono::milliseconds timeout(100); //100ms //std::chrono::steady_clock::now() 当前时间 if (my_mutex.try_lock_until(std::chrono::steady_clock::now() + timeout)) { //再100ms内拿到了锁 msgRecvQueue.push_back(i); cout << "inMsgRecvQueue()执行,插入一个元素:" << i << endl; my_mutex.unlock(); //用完了要解锁 } else { //这次没拿到锁头,休息一下,休息完再尝试 std::chrono::milliseconds sleeptime(100); std::this_thread::sleep_for(sleeptime); } } return; } bool outMsgLULProc(int& command) { my_mutex.lock(); std::chrono::milliseconds cutTime(100000); std::this_thread::sleep_for(cutTime); 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 = outMsgLULProc(command); if (result == true) { cout << "outMsgRecvQueue()执行,取出一个元素" << command << endl; //接下来就考虑处理数据...... } else { //消息队列为空 cout << "outMsgRecvQueue()执行,但目前消息队列中为空" << i << endl; } } } private: list<int> msgRecvQueue; //容器,专门用于代表玩家给咱们发送过来的命令 std::timed_mutex my_mutex; //创建了一个带超时功能的独占互斥量 }; int main() { A myobja; thread myOutnMsgObj(&A::outMsgRecvQueue, &myobja); thread myInMsgObj(&A::inMsgRecvQueue, &myobja); myInMsgObj.join(); myOutnMsgObj.join(); cout << "I live China!" << endl; //最后执行这句,整个进程退出 system("pause"); return 0; }
5.2 std::recursive_timed_mutex
- 带超时功能的递归独占互斥量
- 允许同一个线程多次获取这个互斥量,可以多次
lock()
和unlock()
。 - 同
std::recursive_mutex
一样,只不过和std::timed_mutex
一样,多了一个超时功能。
注:本人学习c++多线程视频地址:C++多线程学习地址