【C++多线程】嵌套锁/递归锁std::recursive_mutex和Windows临界区
Windows临界区
Windows临界区,同一个线程是可以重复进入的,但是进入的次数与离开的次数必须相等。C++互斥量则不允许同一个线程重复加锁。windows临界区是在windows编程中的内容,了解一下即可,效果几乎可以等同于c++11的mutex。包含#include <windows.h>。windows中的临界区同mutex一样,可以保护一个代码段。但windows的临界区可以进入多次,离开多次,但是进入的次数与离开的次数必须相等,不会引起程序报异常出错。
CRITICAL_SECTION对应std::mutex, EnterCriticalSection()对应lock(),LeaveCriticalSection()对应unlock()。
1 #include <iostream> 2 #include <thread> 3 #include <list> 4 #include <mutex> 5 #include <Windows.h> 6 7 #define __WINDOWSJQ_ 8 9 using namespace std; 10 11 class A 12 { 13 public: 14 // 把收到的消息传入队列 15 void inMsgRecvQueue() 16 { 17 for (size_t i = 0; i < 1000; ++i) 18 { 19 cout << "收到消息,并放入队列 " << i << endl; 20 21 #ifdef __WINDOWSJQ_ 22 EnterCriticalSection(&my_winsec); // 进入临界区 23 //EnterCriticalSection(&my_winsec); // 可以再次进入临界区,程序不会出错 24 msgRecvQueue.push_back(i); 25 LeaveCriticalSection(&my_winsec); // 离开临界区 26 //LeaveCriticalSection(&my_winsec); // 如果进入两次,必须离开两次不会报错 27 #elif 28 my_mutex.lock(); 29 msgRecvQueue.push_back(i); 30 my_mutex.unlock(); 31 #endif // __WINDOWSJQ_ 32 } 33 34 cout << "消息入队结束" << endl; 35 } 36 37 // 从队列中取出消息 38 void outMsgRecvQueue() 39 { 40 for (size_t i = 0; i < 1000; ++i) 41 { 42 #ifdef __WINDOWSJQ_ 43 EnterCriticalSection(&my_winsec); // 进入临界区 44 if (!msgRecvQueue.empty()) 45 { 46 // 队列不为空 47 int num = msgRecvQueue.front(); 48 cout << "从消息队列中取出 " << num << endl; 49 msgRecvQueue.pop_front(); 50 } 51 else 52 { 53 // 消息队列为空 54 cout << "消息队列为空 " << endl; 55 } 56 LeaveCriticalSection(&my_winsec); // 离开临界区 57 #elif 58 my_mutex.lock(); 59 if (!msgRecvQueue.empty()) 60 { 61 // 队列不为空 62 int num = msgRecvQueue.front(); 63 cout << "从消息队列中取出 " << num << endl; 64 msgRecvQueue.pop_front(); 65 my_mutex.unlock(); 66 } 67 else 68 { 69 // 消息队列为空 70 cout << "消息队列为空 " << endl; 71 my_mutex.unlock(); 72 } 73 #endif // __WINDOWSJQ_ 74 } 75 76 cout << "消息出队结束" << endl; 77 } 78 79 A() 80 { 81 #ifdef __WINDOWSJQ_ 82 InitializeCriticalSection(&my_winsec); // 用临界区之前要初始化 83 #endif // __WINDOWSJQ_ 84 85 } 86 87 private: 88 list<int> msgRecvQueue; 89 mutex my_mutex; 90 91 #ifdef __WINDOWSJQ_ 92 CRITICAL_SECTION my_winsec; // windows中的临界区,非常类似C++11中的mutex 93 #endif // __WINDOWSJQ_ 94 95 }; 96 97 int main() 98 { 99 A myobj; 100 thread myInMsgObj(&A::inMsgRecvQueue, &myobj); 101 thread myOutMsgObj(&A::outMsgRecvQueue, &myobj); 102 myInMsgObj.join(); 103 myOutMsgObj.join(); 104 105 getchar(); 106 return 0; 107 }
使用RAII实现windows版的lock_guard<>
1 class CWinLock { 2 public: 3 CWinLock(CRITICAL_SECTION *pCritmp) 4 { 5 my_winsec =pCritmp; 6 EnterCriticalSection(my_winsec); 7 } 8 ~CWinLock() 9 { 10 LeaveCriticalSection(my_winsec) 11 }; 12 private: 13 CRITICAL_SECTION *my_winsec; 14 };
std::recursive_mutex嵌套锁/递归锁
std::recursive_mutex 与 std::mutex 一样,也是一种可以被上锁的对象,但是和 std::mutex 不同的是,std::recursive_mutex 允许同一个线程对互斥量多次上锁(即递归上锁),来获得对互斥量对象的多层所有权,std::recursive_mutex 释放互斥量时需要调用与该锁层次深度相同次数的 unlock(),可理解为 lock() 次数和 unlock() 次数相同,除此之外,std::recursive_mutex 的特性和 std::mutex 大致相同。
例如函数a需要获取锁mutex,函数b也需要获取锁mutex,同时函数a中还会调用函数b。如果使用std::mutex必然会造成死锁。但是使用std::recursive_mutex就可以解决这个问题。
参考
菜鸟手记。