std:mutex
在 C++ 中沥匈,mutex 類能用于保護(hù)共享數(shù)據(jù)從多個(gè)線程同時(shí)訪問的同步原語神帅。
mutex 提供排他性非遞歸所有權(quán)語義:
- 調(diào)用方線程從它成功調(diào)用 lock 或者 try_lock 開始,到它調(diào)用 unlock 為止胳岂,占用該 mutex
- 調(diào)用線程占用 mutex编整,所有其它線程試圖要求 mutex 的所有權(quán),如果請求線程調(diào)用 lock(),則將阻塞;如果請求線程調(diào)用 try_lock() 則返回 false乳丰。
如果 mutex 在被線程占用的時(shí)候被銷毀掌测,或者占用 mutex 的時(shí)候線程終止,則行為未定義产园。
mutex 的類信息如下:
(構(gòu)造函數(shù)) | 構(gòu)造互斥 (公開成員函數(shù)) |
---|---|
(析構(gòu)函數(shù)) | 銷毀互斥 (公開成員函數(shù)) |
operator=[被刪除] | 不可復(fù)制賦值 (公開成員函數(shù)) |
鎖定 | |
lock | 鎖定互斥汞斧,若互斥不可用則阻塞 (公開成員函數(shù)) |
try_lock | 嘗試鎖定互斥夜郁,若互斥不可用則返回 (公開成員函數(shù)) |
unlock | 解鎖互斥 (公開成員函數(shù)) |
原生句柄 | |
native_handle | 返回底層實(shí)現(xiàn)定義的原生句柄 (公開成員函數(shù)) |
在實(shí)際的開發(fā)中,不會直接使用 mutex 粘勒,而是使用 std:unique_lock,std::lock_guard,std::scoped_lock 等方式進(jìn)行加鎖竞端。
std::recursive_mutex
和 std::mutex 一樣,但是 std::recursive_mutex 允許同一個(gè)線程對互斥量多次上鎖庙睡,也就是遞歸上鎖事富,來獲得互斥對象的多層所有權(quán),同時(shí)釋放互斥量也要調(diào)用鎖深度層次相同的 unlock() 方法乘陪。
std::time_mutex
std::time_mutex 比 std::mutex 多了兩個(gè)成員函數(shù)统台,try_lock_for(),try_lock_until()啡邑。
try_lock_for() 表示函數(shù)接受一個(gè)時(shí)間范圍贱勃,也就是在這個(gè)時(shí)間段范圍內(nèi)沒有獲取鎖,則該線程被阻塞谤逼。
try_lock_until 函數(shù)則接受一個(gè)時(shí)間點(diǎn)作為參數(shù)贵扰,在指定時(shí)間點(diǎn)未到來之前線程如果沒有獲得鎖則被阻塞住,如果在此期間其他線程釋放了鎖森缠,則該線程可以獲得對互斥量的鎖拔鹰,如果超時(shí)(即在指定時(shí)間內(nèi)還是沒有獲得鎖)仪缸,則返回 false贵涵。
示例代碼如下:
#include <mutex>
#include <thread>
#include <iostream>
std::timed_mutex t_mutex;
void fire_work() {
//try_lock_for 會等待一段時(shí)間,
while (!t_mutex.try_lock_for(chrono::milliseconds(200))) {
std::cout << "-";
}
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
cout << "*\n";
t_mutex.unlock();
}
int main() {
std::thread threads[10];
// spawn 10 threads:
for (int i = 0; i < 10; ++i)
threads[i] = std::thread(fire_work);
for (auto &th : threads) th.join();
return 0;
}
最后控制臺輸入如下:
------------------------------------*
----------------------------------------*
-----------------------------------*
------------------------------*
-------------------------*
--------------------*
---------------*
----------*
-----*
*
示例代碼中恰画,啟動了十個(gè)線程,然后每個(gè)線程都會對互斥信號量 t_mutex宾茂,請求 lock。第一個(gè)線程拴还,拿到了 lock跨晴,然后直接打印了 *,第二個(gè)線程,try_lock 會等待 200 片林。
std::lock_guard
源碼位于頭文件 <mutex> 中端盆,template<class Mutex> class lock_grard
該類是互斥體的包裝器,為在作用域塊期間內(nèi)占用互斥提供便利的 RAII 風(fēng)格機(jī)制费封,也就是資源獲取即初始化機(jī)制焕妙。
從邏輯上來說,創(chuàng)建 lock_guard() 對象時(shí)弓摘,它試圖接受給定互斥的所有權(quán)焚鹊。控制離開創(chuàng)建 lock_guard 對象的作用域時(shí)韧献,銷毀 lock_guard 并且是否互斥末患。
簡單例子如下:
#include <mutex>
#include <thread>
//聲明空間
using namespace std;
int g_i = 0;
mutex g_i_mutex;//定義一個(gè)互斥信息
void safe_increment() {
//創(chuàng)建 lock_guard 對象研叫,并且使用g_i_mutex 作為構(gòu)造函數(shù)參數(shù)
//在整個(gè)方法作用域里面,這鎖都是生效的
lock_guard<std::mutex> lock(g_i_mutex);
//操作需要互斥的變量之前璧针,先創(chuàng)建 lock_guard<std::mutex> 對象
g_i++;
}
int main() {
thread t1(safe_increment);
thread t2(safe_increment);
t1.join();
t2.join();
}
lock_grard 的原理很簡單嚷炉,在對象的構(gòu)造函數(shù)中,調(diào)用 mutex.lock()探橱,然后在析構(gòu)函數(shù)中渤昌,調(diào)用 unlock(),源碼如下:
explicit lock_guard(mutex_type& __m) _LIBCPP_THREAD_SAFETY_ANNOTATION(acquire_capability(__m))
: __m_(__m) {__m_.lock();}
lock_guard(mutex_type& __m, adopt_lock_t) _LIBCPP_THREAD_SAFETY_ANNOTATION(requires_capability(__m))
: __m_(__m) {}
~lock_guard() _LIBCPP_THREAD_SAFETY_ANNOTATION(release_capability()) {__m_.unlock();}
std::unique_lock
源碼位于頭文件 <mutex> 中,template<class Mutex> class unique_lock
和 std::lock_guard() 類似走搁,但是提供了更好的上鎖機(jī)制和解鎖控制独柑。包含了 try_lock_for() 和 try_lock_unitl() 兩個(gè)方法,可以指定等待鎖的時(shí)間私植。
類的相關(guān)信息如下:
(構(gòu)造函數(shù)) | 構(gòu)造 unique_lock 忌栅,可選地鎖定提供的互斥 (公開成員函數(shù)) |
---|---|
(析構(gòu)函數(shù)) | 若占有關(guān)聯(lián)互斥,則解鎖之 (公開成員函數(shù)) |
operator= | 若占有則解鎖互斥曲稼,并取得另一者的所有權(quán) (公開成員函數(shù)) |
鎖定 | |
lock | 鎖定關(guān)聯(lián)互斥 (公開成員函數(shù)) |
try_lock | 嘗試鎖定關(guān)聯(lián)互斥索绪,若互斥不可用則返回 (公開成員函數(shù)) |
try_lock_for | 試圖鎖定關(guān)聯(lián)的可定時(shí)鎖定 (TimedLockable) 互斥,若互斥在給定時(shí)長中不可用則返回 (公開成員函數(shù)) |
try_lock_until | 嘗試鎖定關(guān)聯(lián)可定時(shí)鎖定 (TimedLockable) 互斥贫悄,若抵達(dá)指定時(shí)間點(diǎn)互斥仍不可用則返回 (公開成員函數(shù)) |
unlock | 解鎖關(guān)聯(lián)互斥 (公開成員函數(shù)) |
修改器 | |
swap | 與另一 std::unique_lock 交換狀態(tài) (公開成員函數(shù)) |
release | 將關(guān)聯(lián)互斥解關(guān)聯(lián)而不解鎖它 (公開成員函數(shù)) |
觀察器 | |
mutex | 返回指向關(guān)聯(lián)互斥的指針 (公開成員函數(shù)) |
owns_lock | 測試鎖是否占有其關(guān)聯(lián)互斥 (公開成員函數(shù)) |
operator bool | 測試鎖是否占有其關(guān)聯(lián)互斥 (公開成員函數(shù)) |
示例代碼如下瑞驱,以一個(gè)銀行轉(zhuǎn)賬的業(yè)務(wù)為例子:
void transfer(bank_account &from, bank_account &to, int amount) {
//defer_lock 表示延遲加鎖,實(shí)際上未獲取鎖
unique_lock<mutex> lock1(from.mMutex, defer_lock);
unique_lock<mutex> lock2(to.mMutex, defer_lock);
//獲取鎖
lock(lock1, lock2);
from.iMoney -= amount;
to.iMoney += amount;
}
void test_transfer() {
bank_account Account1("User1", 100);
bank_account Account2("User2", 50);
thread t1([&]() { transfer(Account1, Account2, 10); });//lambda表達(dá)式
thread t2([&]() { transfer(Account2, Account1, 5); });
t1.join();
t2.join();
}
參考