一文講通鎖標記對象std::adopt_lock盲點
- 1. 核心概念
- 2. 代碼詳解
- 1. 單個鎖
- 2. 多重鎖(可以用來預防死鎖)
- 3. 條件變量的互斥控制
- 4. 復雜示例: 多生產者-多消費者模型(超綱了, 可不看,哈哈哈哈)
- 3. 小結
1. 核心概念
??在C++中, std::adopt_lock是一個鎖標記對象[^1], 用于配合鎖對象(如 std::lock_guard、std::unique_lock 或 std::shared_lock)來管理互斥鎖(mutex), 它的作用是告訴鎖對象:互斥鎖已經被手動鎖定了,鎖對象只需要“接管”這個鎖的所有權,而不需要再次嘗試鎖定。
2. 代碼詳解
1. 單個鎖
#include <iostream>
#include <mutex>
#include <thread>std::mutex mtx; // Global mutexvoid process() {// Step 1: Manually lock the mutex.mtx.lock(); // The current thread now owns the mutex.// Step 2: Construct a lock_guard to adopt the existing lock.// The std::adopt_lock tag tells lock_guard that mtx is already locked.std::lock_guard<std::mutex> guard(mtx, std::adopt_lock);// Critical section: safe access to shared resources.std::cout << "Inside critical section." << std::endl;// When 'guard' goes out of scope, its destructor will call mtx.unlock().
}int main() {std::thread t(process);t.join(); // Wait for the thread to finish.return 0;
}
??這里的關鍵點是 std::adopt_lock,它告訴 lock_guard 互斥量已經被鎖定,因此 lock_guard 不會嘗試再次調用
mtx.lock()。
如果沒有使用 std::adopt_lock,std::lock_guard 會在構造時試圖鎖定互斥量,這將導致死鎖。
2. 多重鎖(可以用來預防死鎖)
#include <iostream>
#include <mutex>
#include <thread>std::mutex mtx1;
std::mutex mtx2;void task() {// Step 1: Atomically lock both mutexes using std::lock, which prevents deadlock.std::lock(mtx1, mtx2);// Step 2: Create RAII objects that adopt these locks.std::lock_guard<std::mutex> guard1(mtx1, std::adopt_lock);std::lock_guard<std::mutex> guard2(mtx2, std::adopt_lock);// Critical section: safe access to shared resources protected by mtx1 and mtx2.std::cout << "Thread safely acquired both mutexes." << std::endl;// Both mutexes will be unlocked when guard1 and guard2 go out of scope.
}int main() {std::thread t1(task);std::thread t2(task);t1.join();t2.join();return 0;
}
3. 條件變量的互斥控制
??在條件變量中結合 std::unique_lock 和 std::adopt_lock 使用,可以靈活地管理鎖的狀態。
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>std::mutex mtx;
std::condition_variable cv;
bool ready = false;void workerThread() {// 手動鎖定互斥量mtx.lock();std::cout << "Worker thread acquired lock manually.\n";// 使用 adopt_lock 轉移鎖管理權std::unique_lock<std::mutex> lock(mtx, std::adopt_lock);// 等待條件變量被通知cv.wait(lock, [] { return ready; });std::cout << "Worker thread is processing data.\n";
}void notifierThread() {std::this_thread::sleep_for(std::chrono::seconds(1));// 通知條件變量{std::lock_guard<std::mutex> lock(mtx); // 自動管理鎖ready = true;std::cout << "Notifier thread is notifying.\n";}cv.notify_one();
}int main() {std::thread worker(workerThread);std::thread notifier(notifierThread);worker.join();notifier.join();return 0;
}
解釋補充
- worker_thread 手動鎖定互斥量并通過 std::adopt_lock 轉移鎖管理權給 std::unique_lock。
- notifier_thread 通過 std::lock_guard 安全通知條件變量。
4. 復雜示例: 多生產者-多消費者模型(超綱了, 可不看,哈哈哈哈)
??假設有多個生產者線程向共享隊列中添加任務,而多個消費者線程從隊列中處理任務。為了避免死鎖,使用 std::adopt_lock 配合多個互斥量和條件變量。
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <vector>
#include <chrono>std::mutex queue_mutex; // 隊列的互斥量
std::mutex print_mutex; // 打印輸出的互斥量
std::condition_variable cv; // 條件變量
std::queue<int> task_queue; // 共享任務隊列
const int MAX_QUEUE_SIZE = 10; // 隊列的最大容量
bool stop = false; // 用于通知消費者線程停止// 生產者函數
void producer(int id) {for (int i = 0; i < 20; ++i) {std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 模擬生產延遲std::unique_lock<std::mutex> lock(queue_mutex); // 鎖定隊列的互斥量cv.wait(lock, [] { return task_queue.size() < MAX_QUEUE_SIZE; }); // 等待隊列有空間task_queue.push(i);{// 使用 adopt_lock 安全打印日志std::lock_guard<std::mutex> print_lock(print_mutex, std::adopt_lock);std::cout << "Producer " << id << " produced task " << i << ". Queue size: " << task_queue.size() << std::endl;}cv.notify_all(); // 通知消費者}
}// 消費者函數
void consumer(int id) {while (true) {std::unique_lock<std::mutex> lock(queue_mutex);cv.wait(lock, [] { return !task_queue.empty() || stop; }); // 等待隊列有任務或停止信號if (stop && task_queue.empty()) break; // 如果停止并且隊列為空,退出int task = task_queue.front();task_queue.pop();{// 使用 adopt_lock 安全打印日志std::lock_guard<std::mutex> print_lock(print_mutex, std::adopt_lock);std::cout << "Consumer " << id << " consumed task " << task << ". Queue size: " << task_queue.size() << std::endl;}cv.notify_all(); // 通知生產者}
}int main() {std::vector<std::thread> producers;std::vector<std::thread> consumers;// 啟動生產者線程for (int i = 0; i < 3; ++i) {producers.emplace_back(producer, i + 1);}// 啟動消費者線程for (int i = 0; i < 2; ++i) {consumers.emplace_back(consumer, i + 1);}// 等待所有生產者完成for (auto& p : producers) {p.join();}// 通知消費者停止{std::lock_guard<std::mutex> lock(queue_mutex);stop = true;}cv.notify_all();// 等待所有消費者完成for (auto& c : consumers) {c.join();}std::cout << "All tasks processed. Exiting program." << std::endl;return 0;
}
3. 小結
std::adopt_lock 適用于以下場景:
- Lock First, Adopt Later:手動鎖定互斥量后,將管理權交給鎖管理類(如 std::lock_guard 或 std::unique_lock)。
- 注釋要清晰(就是這么突兀! 沒錯, 就是沒有這個場景!!!, 想想我為什么要寫?還標紅加粗)
- 多個互斥量的協調鎖定。
- 配合條件變量靈活管理鎖定邏輯。
[^1] 鎖標記對象