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

第十二节 windows临界区、其他各种mutex互斥量

在这里插入图片描述

一和二、windows临界区
Windows临界区,同一个线程是可以重复进入的,但是进入的次数与离开的次数必须相等。
C++互斥量则不允许同一个线程重复加锁。

windows临界区是在windows编程中的内容,了解一下即可,效果几乎可以等同于c++11的mutex
包含#include
windows中的临界区同mutex一样,可以保护一个代码段。但windows的临界区可以进入多次,离开多次,但是进入的次数与离开的次数必须相等,不会引起程序报异常出错。

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
#include <iostream>
#include <thread>
#include <list>
#include <mutex>
#include <Windows.h>
#define __WINDOWSJQ_
using namespace std;
class A
{
public:
// 把收到的消息传入队列
void inMsgRecvQueue()
{
for (size_t i = 0; i < 1000; ++i)
{
cout << "收到消息,并放入队列 " << i << endl;
#ifdef __WINDOWSJQ_
EnterCriticalSection(&my_winsec); // 进入临界区
//EnterCriticalSection(&my_winsec); // 可以再次进入临界区,程序不会出错
msgRecvQueue.push_back(i);
LeaveCriticalSection(&my_winsec); // 离开临界区
//LeaveCriticalSection(&my_winsec); // 如果进入两次,必须离开两次不会报错
#elif
my_mutex.lock();
msgRecvQueue.push_back(i);
my_mutex.unlock();
#endif // __WINDOWSJQ_
}
cout << "消息入队结束" << endl;
}
// 从队列中取出消息
void outMsgRecvQueue()
{
for (size_t i = 0; i < 1000; ++i)
{
#ifdef __WINDOWSJQ_
EnterCriticalSection(&my_winsec); // 进入临界区
if (!msgRecvQueue.empty())
{
// 队列不为空
int num = msgRecvQueue.front();
cout << "从消息队列中取出 " << num << endl;
msgRecvQueue.pop_front();
}
else
{
// 消息队列为空
cout << "消息队列为空 " << endl;
}
LeaveCriticalSection(&my_winsec); // 离开临界区
#elif
my_mutex.lock();
if (!msgRecvQueue.empty())
{
// 队列不为空
int num = msgRecvQueue.front();
cout << "从消息队列中取出 " << num << endl;
msgRecvQueue.pop_front();
my_mutex.unlock();
}
else
{
// 消息队列为空
cout << "消息队列为空 " << endl;
my_mutex.unlock();
}
#endif // __WINDOWSJQ_
}
cout << "消息出队结束" << 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 myobj;
thread myInMsgObj(&A::inMsgRecvQueue, &myobj);
thread myOutMsgObj(&A::outMsgRecvQueue, &myobj);
myInMsgObj.join();
myOutMsgObj.join();
getchar();
return 0;
}

三、自动析构技术
C++:lock_guard防止忘了释放信号量,自动释放
windows:可以写个类自动释放临界区:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class CWinLock {
public:
CWinLock(CRITICAL_SECTION *pCritmp)
{
my_winsec =pCritmp;
EnterCriticalSection(my_winsec);
}
~CWinLock()
{
LeaveCriticalSection(my_winsec)
};
private:
CRITICAL_SECTION *my_winsec;
};

上述这种类RAII类(Resource Acquisition is initialization),即资源获取及初始化。容器,智能指针属于这种类。

四、递归独占互斥量 std::recursive_mutex
std::mutex 独占式互斥量

std::recursive_mutex:允许在同一个线程中同一个互斥量多次被 lock() ,(但是递归加锁的次数是有限制的,太多可能会报异常),效率要比mutex低。

如果你真的用了 recursive_mutex 要考虑代码是否有优化空间,如果能调用一次 lock()就不要调用多次。

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

5.1 std::timed_mutex:是待超时的独占互斥量

  • try_lock_for():

等待一段时间,如果拿到了锁,或者超时了未拿到锁,就继续执行(有选择执行)如下:

1
2
3
4
5
6
7
8
std::chrono::milliseconds timeout(100);
if (my_mymutex.try_lock_for(timeout)){
//......拿到锁返回ture
}
else{
std::chrono::milliseconds sleeptime(100);
std::this_thread::sleep_for(sleeptime);
}
  • try_lock_until():

参数是一个未来的时间点,在这个未来的时间没到的时间内,如果拿到了锁头,流程就走下来,如果时间到了没拿到锁,流程也可以走下来。

1
2
3
4
5
6
7
8
std::chrono::milliseconds timeout(100);
if (my_mymutex.try_lock_until(chrono::steady_clock::now() + timeout)){
//......拿到锁返回ture
}
else{
std::chrono::milliseconds sleeptime(100);
std::this_thread::sleep_for(sleeptime);
}

两者的区别就是一个参数是时间段,一个参数是时间点

5.2 std::recursive_timed_mutex:是待超时的递归独占互斥量