1、Windows临界区
- Window也提供了类似于C++11多线程库中的std::mutex互斥量锁,被叫做关键区域(CRITICAL_SECTION),在头文件Windows.h中可以找到
- CRITICAL_SECTION类型:定义一个关键区域变量my_sec
- InitializeCriticalSection(&my_sec);初始化这个变量
- 当需要对一段代码段进行串行执行(线程安全)时,可以通过EnterCriticalSection(&my_sec);来加锁
- 访问结束通过LeaveCriticalSection(&my_sec);释放这个临界区域的锁
// Recursive_Timed_Mutex.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <mutex>
#include <queue>
#include <vector>
#include <future>
#include <Windows.h>
#define __WINDOWS_MUTEX__
#define maxn 100000
class MessageQueue {
public:
MessageQueue() {
#ifdef __WINDOWS_MUTEX__
InitializeCriticalSection(&my_sec);
#endif
}
void inMsg(){
for (int i = 0; i < maxn; i++) {
#ifdef __WINDOWS_MUTEX__
EnterCriticalSection(&my_sec);
q.push(i);
std::cout << "放入元素, msg = " << i << std::endl;
LeaveCriticalSection(&my_sec);
#endif
// mutex_lock.lock();
// q.push(i);
// std::cout << "放入元素, msg = " << i << std::endl;
// mutex_lock.unlock();
}
}
void outMsg(){
for (int i = 0; i < maxn; i++) {
#ifdef __WINDOWS_MUTEX__
EnterCriticalSection(&my_sec);
// EnterCriticalSection(&my_sec);
get_msg();
LeaveCriticalSection(&my_sec);
// LeaveCriticalSection(&my_sec);
#endif
// mutex_lock.lock();
// get_msg();
// mutex_lock.unlock();
}
}
void get_msg(){
if (q.size()) {
int msg = q.front();
q.pop();
std::cout << "msg = " << msg << std::endl;
}
else {
std::cout << "没有取到数据" << std::endl;
}
}
private:
std::queue<int> q;
std::mutex mutex_lock;
#ifdef __WINDOWS_MUTEX__
CRITICAL_SECTION my_sec;
#endif
};
void test1_CRITICAL_SECTION() {
MessageQueue messageQueue;
std::thread inthread(&MessageQueue::inMsg, &messageQueue);
std::thread outthread(&MessageQueue::outMsg, &messageQueue);
outthread.join();
inthread.join();
}
int main()
{
test1_CRITICAL_SECTION();
std::cout << "Hello World!\n" << std::endl;
return 0;
}
-
但这个关键区域(CRITICAL_SECTION)与互斥量(Mutex)存在一定的差异,关键区域是可以进行锁重入的,而Mutex并不支持。
- 锁重入:同一个线程可以对同一把锁多次获取
- 获取多少次需要对应的释放多少次
EnterCriticalSection(&my_sec); // EnterCriticalSection(&my_sec); get_msg(); LeaveCriticalSection(&my_sec); // LeaveCriticalSection(&my_sec);
2、RAII技术
- RAII:全称Resource Acquisition is Initialization,资源获取即初始化,这个东西在C++11中还是很常见的一个东西。
- 例如多线程中的std::lock_guard模板类,在默认情况下传入一个mutex构造时就自动加锁,析构时自动释放锁
- 底层源码中可以看到构造函数里自动对传入的mutex进行lock,而在析构函数时自动unlock。
- lock_guard还是存在一定问题的,无法提前释放锁,需要等到这个模板类析构时才释放,如果需要在析构之前释放那么析构就会报错(锁的重复释放)
- 对应的unique_lock也是一个RAII,但是unique_lock不仅含有lock_guard所有的功能,而且可以提前释放锁,在析构时会判断锁是否已经释放!
template<typename _Mutex>
class lock_guard
{
public:
typedef _Mutex mutex_type;
explicit lock_guard(mutex_type& __m) : _M_device(__m) {
_M_device.lock();
}
lock_guard(mutex_type& __m, adopt_lock_t) noexcept : _M_device(__m) { } // calling thread owns mutex
~lock_guard(){
_M_device.unlock();
}
lock_guard(const lock_guard&) = delete;
lock_guard& operator=(const lock_guard&) = delete;
private:
mutex_type& _M_device;
};
3、递归互斥量与超时互斥量
为了实现锁的重入和超时等待,C++11中同时也引入了可重入和超时mutex:
- std::recursive_mutex
- std::timed_mutex
- std::recursive_timed_mutex
3.1、std::recursive_mutex
-
recursive_mutex递归的独占互斥量(可重入锁),允许同一个线程对同一个互斥量进行多次lock,效率上比mutex要差一些,重入的次数有限制,次数太多可能报异常
-
其实就是一个可以重复加锁的mutex互斥量
-
在使用时应该尽量的减少这个锁的使用,应该优先考虑程序的优化
例如下面这段代码,递归的计算1 + 2 + … + n的值,为了防止锁被占用导致多线程的错误采用可重入锁,其实这种情况应该采用单互斥量 + 循环或者 + 等差数列计算公式来做这个数列求和。
int f(int n){
std::unique_lock<std::recursive_mutex> uniqueLock;
if(n == 1){
return 1;
}
return n + f(n - 1);
}
3.2、std::timed_mutex
- 带超时功能的独占互斥量,防止出现死等的情况
- timed_mutex主要提供了两种方法来检测超时
- try_lock_for:等待一个指定的时间长度,如5秒
- try_lock_until:等到到一个未来的时间点,如当前11点,等到12点。
std::timed_mutex timedMutex;
int a = 10;
void inMsg()
{
timedMutex.lock();
std::chrono::seconds sleeptime(3);
std::this_thread::sleep_for(sleeptime);
a = 2000;
timedMutex.unlock();
}
void outMsg()
{
std::chrono::seconds wait_time(5);
if(timedMutex.try_lock_for(wait_time)){
std::cout << "等到了!a = " << a << std::endl;
timedMutex.unlock();
}
else {
std::cout << "没等到!" << std::endl;
};
}
void test_timed_mutex()
{
std::thread inthread(inMsg);
std::thread outthread(outMsg);
inthread.join();
outthread.join();
}
3.3、std::recursive_timed_mutex
带超时功能的递归互斥量,std::recursive_mutex和std::timed_mutex的结合版本,也没什么新奇的东西。基本上就是Mutex衍生的东西。