具有共享/獨占訪問權限,且具有升級/降級功能的互斥鎖
介紹
我的目標是創建可以充當讀/寫鎖定機制的物件,任何執行緒都可以鎖定它以進行讀取,但是只有一個執行緒可以鎖定它以進行寫入,在寫入執行緒釋放它之前,所有其他執行緒都將等待,在釋放任何其他執行緒之前,寫執行緒不會獲取互斥體,
我可以使用Slim Reader / Writer鎖,但是:
- 它們不是遞回的,例如,
AcquireSRWLockExclusive()如果同一執行緒較早呼叫同一函式,則對的呼叫將阻塞, - 它們不可升級,例如,已將鎖鎖定為讀取訪問權限的執行緒無法將其鎖定為寫入操作,
- 它們不是可復制的句柄,
我可以嘗試C ++ 14,shared_lock但是我仍然需要C ++ 11支持,此外,我還不確定它是否可以真正滿足我的要求,
因此,我不得不手動實作它,由于缺少,洗掉了普通的C ++ 11方法WaitForMultipleObjects (nyi),現在具有升級/降級功能,
RWMUTEX
這一節很簡單,
1 class RWMUTEX 2 { 3 private: 4 HANDLE hChangeMap; 5 std::map<DWORD, HANDLE> Threads; 6 RWMUTEX(const RWMUTEX&) = delete; 7 RWMUTEX(RWMUTEX&&) = delete;
我需要std::map<DWORD,HANDLE>為所有嘗試訪問共享資源的執行緒存盤一個句柄,并且還需要一個互斥鎖以確保對此映射的所有更改都是執行緒安全的,
建構式
1 RWMUTEX(const RWMUTEX&) = delete; 2 void operator =(const RWMUTEX&) = delete; 3 4 RWMUTEX() 5 { 6 hChangeMapWrite = CreateMutex(0,0,0); 7 }
簡單地創建一個映射互斥的句柄,物件不可復制,
創建
1 HANDLE CreateIf(bool KeepReaderLocked = false) 2 { 3 WaitForSingleObject(hChangeMap, INFINITE); 4 DWORD id = GetCurrentThreadId(); 5 if (Threads[id] == 0) 6 { 7 HANDLE e0 = CreateMutex(0, 0, 0); 8 Threads[id] = e0; 9 } 10 HANDLE e = Threads[id]; 11 if (!KeepReaderLocked) 12 ReleaseMutex(hChangeMap); 13 return e; 14 }
當呼叫LockRead()或LockWrite()來鎖定物件時,將呼叫這個私有函式,如果當前執行緒還沒有將自己變為可能訪問這個互斥鎖的執行緒中,這個函式將為該執行緒創建一個互斥鎖,如果其他執行緒已經鎖定這個互斥物件進行寫訪問,那么這個函式就會阻塞,直到寫執行緒釋放這個物件為止,這個函式回傳當前執行緒的互斥句柄,
鎖定讀取/釋放讀取
1 HANDLE LockRead() 2 { 3 auto f = CreateIf(); 4 WaitForSingleObject(f,INFINITE); 5 return f; 6 } 7 void ReleaseRead(HANDLE f) 8 { 9 ReleaseMutex(f); 10 }
當您要鎖定物件以進行讀取訪問并稍后釋放它時,將呼叫這些函式,
鎖/釋放
1 void LockWrite() 2 { 3 CreateIf(true); 4 5 // Wait for all 6 vector<HANDLE> AllThreads; 7 AllThreads.reserve(Threads.size()); 8 for (auto& a : Threads) 9 { 10 AllThreads.push_back(a.second); 11 } 12 13 WaitForMultipleObjects((DWORD)AllThreads.size(), AllThreads.data(), TRUE, INFINITE); 14 15 // Reader is locked 16 } 17 18 void ReleaseWrite() 19 { 20 21 // Release All 22 for (auto& a : Threads) 23 ReleaseMutex(a.second); 24 ReleaseMutex(hChangeMap); 25 }
當您希望鎖定物件以進行寫訪問并在稍后釋放它時,將呼叫這些函式,函式的作用是:
1.在鎖期間沒有注冊新執行緒
2.任何讀取執行緒都釋放了鎖
解構式
1 RWMUTEX() 2 { 3 CloseHandle(hChangeMap); 4 hChangeMap = 0; 5 for (auto& a : Threads) 6 CloseHandle(a.second); 7 Threads.clear(); 8 }
解構式確保清除所有句柄,
可升級/可升級鎖
有時,您希望將讀鎖升級為寫鎖,而不先解鎖,以提高效率,因此,LockWrite被修改為:
1 void LockWrite(DWORD updThread = 0) 2 { 3 CreateIf(true); 4 5 // Wait for all 6 AllThreads.reserve(Threads.size()); 7 AllThreads.clear(); 8 for (auto& a : Threads) 9 { 10 if (updThread == a.first) // except ourself if in upgrade operation 11 continue; 12 AllThreads.push_back(a.second); 13 } 14 auto tim = WaitForMultipleObjects((DWORD)AllThreads.size(), AllThreads.data(), TRUE, wi); 15 16 if (tim == WAIT_TIMEOUT && wi != INFINITE) 17 OutputDebugString(L"LockWrite debug timeout!"); 18 19 // We don't want to keep threads, the hChangeMap is enough 20 // We also release the handle to the upgraded thread, if any 21 for (auto& a : Threads) 22 ReleaseMutex(a.second); 23 24 // Reader is locked 25 } 26 27 void Upgrade() 28 { 29 LockWrite(GetCurrentThreadId()); 30 } 31 32 HANDLE Downgrade() 33 { 34 DWORD id = GetCurrentThreadId(); 35 auto z = Threads[id]; 36 auto tim = WaitForSingleObject(z, wi); 37 if (tim == WAIT_TIMEOUT && wi != INFINITE) 38 OutputDebugString(L"Downgrade debug timeout!"); 39 ReleaseMutex(hChangeMap); 40 return z; 41 }
呼叫Upgrade()現在的結果是:
更改被鎖定的映射
等待除我們自己的執行緒之外的所有讀執行緒退出
然后我們釋放我們自己的執行緒互斥鎖,因為更改鎖定的映射就足夠了,
呼叫Downgrade()結果:
- 直接從映射上獲取手柄,無需重新鎖定
- 鎖定此句柄,就像我們處于讀取模式一樣
- 發布變更映射
因此,整個代碼是(帶有一些除錯幫助):
1 // RWMUTEX 2 class RWMUTEX 3 { 4 private: 5 HANDLE hChangeMap = 0; 6 std::map<DWORD, HANDLE> Threads; 7 DWORD wi = INFINITE; 8 RWMUTEX(const RWMUTEX&) = delete; 9 RWMUTEX(RWMUTEX&&) = delete; 10 operator=(const RWMUTEX&) = delete; 11 12 public: 13 14 RWMUTEX(bool D = false) 15 { 16 if (D) 17 wi = 10000; 18 else 19 wi = INFINITE; 20 hChangeMap = CreateMutex(0, 0, 0); 21 } 22 23 ~RWMUTEX() 24 { 25 CloseHandle(hChangeMap); 26 hChangeMap = 0; 27 for (auto& a : Threads) 28 CloseHandle(a.second); 29 Threads.clear(); 30 } 31 32 HANDLE CreateIf(bool KeepReaderLocked = false) 33 { 34 auto tim = WaitForSingleObject(hChangeMap, INFINITE); 35 if (tim == WAIT_TIMEOUT && wi != INFINITE) 36 OutputDebugString(L"LockRead debug timeout!"); 37 DWORD id = GetCurrentThreadId(); 38 if (Threads[id] == 0) 39 { 40 HANDLE e0 = CreateMutex(0, 0, 0); 41 Threads[id] = e0; 42 } 43 HANDLE e = Threads[id]; 44 if (!KeepReaderLocked) 45 ReleaseMutex(hChangeMap); 46 return e; 47 } 48 49 HANDLE LockRead() 50 { 51 auto z = CreateIf(); 52 auto tim = WaitForSingleObject(z, wi); 53 if (tim == WAIT_TIMEOUT && wi != INFINITE) 54 OutputDebugString(L"LockRead debug timeout!"); 55 return z; 56 } 57 58 void LockWrite(DWORD updThread = 0) 59 { 60 CreateIf(true); 61 62 // Wait for all 63 AllThreads.reserve(Threads.size()); 64 AllThreads.clear(); 65 for (auto& a : Threads) 66 { 67 if (updThread == a.first) // except ourself if in upgrade operation 68 continue; 69 AllThreads.push_back(a.second); 70 } 71 auto tim = WaitForMultipleObjects((DWORD)AllThreads.size(), AllThreads.data(), TRUE, wi); 72 73 if (tim == WAIT_TIMEOUT && wi != INFINITE) 74 OutputDebugString(L"LockWrite debug timeout!"); 75 76 // We don't want to keep threads, the hChangeMap is enough 77 // We also release the handle to the upgraded thread, if any 78 for (auto& a : Threads) 79 ReleaseMutex(a.second); 80 81 // Reader is locked 82 } 83 84 void ReleaseWrite() 85 { 86 ReleaseMutex(hChangeMap); 87 } 88 89 void ReleaseRead(HANDLE f) 90 { 91 ReleaseMutex(f); 92 } 93 94 void Upgrade() 95 { 96 LockWrite(GetCurrentThreadId()); 97 } 98 99 HANDLE Downgrade() 100 { 101 DWORD id = GetCurrentThreadId(); 102 auto z = Threads[id]; 103 auto tim = WaitForSingleObject(z, wi); 104 if (tim == WAIT_TIMEOUT && wi != INFINITE) 105 OutputDebugString(L"Downgrade debug timeout!"); 106 ReleaseMutex(hChangeMap); 107 return z; 108 } 109 };
要使用RWMUTEX,可以簡單地創建鎖定類:
1 class RWMUTEXLOCKREAD 2 { 3 private: 4 RWMUTEX* mm = 0; 5 public: 6 7 RWMUTEXLOCKREAD(const RWMUTEXLOCKREAD&) = delete; 8 void operator =(const RWMUTEXLOCKREAD&) = delete; 9 10 RWMUTEXLOCKREAD(RWMUTEX*m) 11 { 12 if (m) 13 { 14 mm = m; 15 mm->LockRead(); 16 } 17 } 18 ~RWMUTEXLOCKREAD() 19 { 20 if (mm) 21 { 22 mm->ReleaseRead(); 23 mm = 0; 24 } 25 } 26 }; 27 28 class RWMUTEXLOCKWRITE 29 { 30 private: 31 RWMUTEX* mm = 0; 32 public: 33 RWMUTEXLOCKWRITE(RWMUTEX*m) 34 { 35 if (m) 36 { 37 mm = m; 38 mm->LockWrite(); 39 } 40 } 41 ~RWMUTEXLOCKWRITE() 42 { 43 if (mm) 44 { 45 mm->ReleaseWrite(); 46 mm = 0; 47 } 48 } 49 };
還有一個用于升級機制的新類:
1 class RWMUTEXLOCKREADWRITE 2 { 3 private: 4 RWMUTEX* mm = 0; 5 HANDLE lm = 0; 6 bool U = false; 7 public: 8 9 RWMUTEXLOCKREADWRITE(const RWMUTEXLOCKREADWRITE&) = delete; 10 void operator =(const RWMUTEXLOCKREADWRITE&) = delete; 11 12 RWMUTEXLOCKREADWRITE(RWMUTEX*m) 13 { 14 if (m) 15 { 16 mm = m; 17 lm = mm->LockRead(); 18 } 19 } 20 21 void Upgrade() 22 { 23 if (mm && !U) 24 { 25 mm->Upgrade(); 26 lm = 0; 27 U = 1; 28 } 29 } 30 31 void Downgrade() 32 { 33 if (mm && U) 34 { 35 lm = mm->Downgrade(); 36 U = 0; 37 } 38 } 39 40 ~RWMUTEXLOCKREADWRITE() 41 { 42 if (mm) 43 { 44 if (U) 45 mm->ReleaseWrite(); 46 else 47 mm->ReleaseRead(lm); 48 lm = 0; 49 mm = 0; 50 } 51 } 52 };
用法示例:
1 RWMUTEX m; 2 3 // ... other code 4 void foo1() { 5 RWMUTEXLOCKREAD lock(&m); 6 } 7 8 void foo2() { 9 RWMUTEXLOCKWRITE lock(&m); 10 }
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/98172.html
標籤:C++
下一篇:Stream API
