目錄
1.自動鎖定與解鎖機制
2.靈活性
3.所有權轉移
4.可與條件變量配合使用
5.性能開銷
在 C++ 中,std::unique_lock
?和?std::lock_guard
?都屬于標準庫?<mutex>
?中的互斥鎖管理工具,用于簡化互斥鎖的使用并確保線程安全。但它們存在一些顯著區別,下面為你詳細介紹:
1.自動鎖定與解鎖機制
1) std::lock_guard
:一個輕量級的互斥鎖包裝器,采用了 RAII(資源獲取即初始化)技術。當?std::lock_guard
?對象被創建時,它會自動鎖定所關聯的互斥鎖;當對象離開其作用域時,會自動解鎖該互斥鎖。它的設計遵循最小化原則,僅提供最基本的鎖管理功能,沒有額外的開銷。其核心實現原理可以簡化為:
template<classMutex>
classlock_guard?{
public:explicitlock_guard(Mutex& m)?: mutex(m) {mutex.lock();}~lock_guard() {mutex.unlock();}lock_guard(const?lock_guard&) =?delete;lock_guard&?operator=(const?lock_guard&) =?delete;private:Mutex& mutex;
};
????????示例代碼如下:
#include <iostream>
#include <mutex>
#include <thread>std::mutex mtx;void printMessage() {std::lock_guard<std::mutex> lock(mtx);std::cout << "This message is protected by lock_guard." << std::endl;// 當 lock_guard 對象離開作用域時,互斥鎖會自動解鎖
}int main() {std::thread t(printMessage);t.join();return 0;
}
2)?std::unique_lock
:?同樣基于 RAII 技術,在對象銷毀時會自動解鎖互斥鎖。不過,它的鎖定和解鎖操作更加靈活,可以在對象創建時選擇不立即鎖定互斥鎖,也可以在對象生命周期內手動鎖定和解鎖。unique_lock 是 C++11 標準中引入的更高級的鎖管理器,設計目標是提供更靈活的鎖管理能力。其核心接口包括:
class?unique_lock?{
public:// 構造時可選立即加鎖、延遲加鎖或嘗試加鎖unique_lock(mutex_type& m, std::defer_lock_t)?noexcept;unique_lock(mutex_type& m, std::try_to_lock_t);unique_lock(mutex_type& m, std::adopt_lock_t);// 轉移構造函數unique_lock(unique_lock&& other)?noexcept;// 手動控制接口voidlock();booltry_lock();voidunlock();// 狀態查詢explicitoperatorbool()constnoexcept;boolowns_lock()constnoexcept;
};
? ? ? ? 示例代碼如下:
#include <iostream>
#include <mutex>
#include <thread>std::mutex mtx;void printMessage() {std::unique_lock<std::mutex> lock(mtx, std::defer_lock);// 手動鎖定互斥鎖lock.lock();std::cout << "This message is protected by unique_lock." << std::endl;// 手動解鎖互斥鎖lock.unlock();
}int main() {std::thread t(printMessage);t.join();return 0;
}
std::unique_lock
?允許在對象生命周期內多次手動調用?lock()
、unlock()
?和?try_lock()
?方法。這在需要在臨界區內進行部分操作后暫時釋放鎖,執行一些非關鍵操作,然后再次鎖定的場景中很有用。如:
#include <iostream>
#include <mutex>
#include <thread>std::mutex mtx;void work() {std::unique_lock<std::mutex> lock(mtx);std::cout << "Entered critical section." << std::endl;// 執行部分臨界區操作lock.unlock();std::cout << "Temporarily released the lock." << std::endl;// 執行一些非關鍵操作lock.lock();std::cout << "Re - entered critical section." << std::endl;// 繼續執行臨界區操作
}int main() {std::thread t(work);t.join();return 0;
}
2.靈活性
1)std::lock_guard:?功能相對單一,缺乏靈活性。一旦創建,就會立即鎖定互斥鎖,并且在其生命周期內無法手動解鎖,只能在對象離開作用域時自動解鎖。
2)std::unique_lock:
具有更高的靈活性。它支持三種鎖定策略:
std::defer_lock_t? ?// 延遲鎖定
std::try_to_lock_t??// 嘗試鎖定
std::adopt_lock_t? ?// 接管已鎖定狀態
- 延遲鎖定(
std::defer_lock
):創建?std::unique_lock
?對象時,可以選擇不立即鎖定互斥鎖。這在需要先進行一些準備工作,之后再鎖定互斥鎖的場景中非常有用。如:
#include <iostream>
#include <mutex>
#include <thread>std::mutex mtx;void work() {std::unique_lock<std::mutex> lock(mtx, std::defer_lock);// 進行一些無需加鎖的準備工作std::cout << "Doing some preparation work..." << std::endl;lock.lock();std::cout << "Critical section entered." << std::endl;// 臨界區代碼lock.unlock();std::cout << "Critical section exited." << std::endl;
}int main() {std::thread t(work);t.join();return 0;
}
- 嘗試鎖定(
std::try_to_lock
):使用?std::try_to_lock
?可以嘗試鎖定互斥鎖,如果互斥鎖當前已被其他線程鎖定,std::unique_lock
?對象不會阻塞,而是立即返回,可通過?owns_lock()
?方法判斷是否成功鎖定。如:
#include <iostream>
#include <mutex>
#include <thread>std::mutex mtx;void work() {std::unique_lock<std::mutex> lock(mtx, std::try_to_lock);if (lock.owns_lock()) {std::cout << "Successfully locked the mutex." << std::endl;// 臨界區代碼} else {std::cout << "Failed to lock the mutex, doing other work." << std::endl;// 執行其他不需要鎖定互斥鎖的工作}
}int main() {std::thread t(work);t.join();return 0;
}
3.所有權轉移
std::lock_guard
:- 不支持所有權轉移,即不能將一個?
std::lock_guard
?對象的互斥鎖所有權轉移給另一個對象。
- 不支持所有權轉移,即不能將一個?
std::unique_lock
:- 支持所有權轉移,可以通過移動構造函數或移動賦值運算符將互斥鎖的所有權從一個?
std::unique_lock
?對象轉移到另一個對象。這在函數返回?std::unique_lock
?對象或需要在不同作用域之間傳遞鎖的所有權時非常有用。 - 示例代碼如下:
- 支持所有權轉移,可以通過移動構造函數或移動賦值運算符將互斥鎖的所有權從一個?
#include <iostream>
#include <mutex>
#include <thread>std::mutex mtx;std::unique_lock<std::mutex> getLock() {std::unique_lock<std::mutex> lock(mtx);return std::move(lock);
}void printMessage() {std::unique_lock<std::mutex> lock = getLock();std::cout << "This message is protected by transferred unique_lock." << std::endl;
}int main() {std::thread t(printMessage);t.join();return 0;
}
4.可與條件變量配合使用
std::unique_lock
?能夠與?std::condition_variable
?一起使用,std::condition_variable
?的?wait()
、wait_for()
?和?wait_until()
?等方法要求傳入?std::unique_lock
?對象,因為在等待期間需要釋放和重新獲取鎖。
#include <iostream>
#include <mutex>
#include <condition_variable>
#include <thread>std::mutex mtx;
std::condition_variable cv;
bool ready = false;void worker() {std::unique_lock<std::mutex> lock(mtx);cv.wait(lock, []{ return ready; });std::cout << "Worker thread is working." << std::endl;
}int main() {std::thread t(worker);{std::lock_guard<std::mutex> lock(mtx);ready = true;}cv.notify_one();t.join();return 0;
}
5.性能開銷
std::lock_guard
:- 由于其功能簡單,沒有額外的狀態管理,因此性能開銷相對較小,適合用于簡單的鎖定場景。
std::unique_lock
:- 為了支持更多的靈活性,
std::unique_lock
?需要維護更多的狀態信息,因此性能開銷相對較大。在對性能要求極高且鎖定邏輯簡單的場景下,使用?std::lock_guard
?更為合適。
- 為了支持更多的靈活性,
綜上所述,std::lock_guard
?適用于簡單的鎖定場景,追求簡潔性和較低的性能開銷;而?std::unique_lock
?則適用于需要更復雜鎖定邏輯、支持所有權轉移的場景,但會帶來一定的性能開銷。
推薦閱讀: