目錄
1. 線程安全與互斥鎖(std::mutex)
2. 互斥量死鎖
3.?std::lock_guard
4.?std::unique_lock
(1)示例
?(2)詳細知識點
5. std::this_thread
(1)sleep_for
(2)sleep_until
(3)yield
(4)get_id
直接通過示例講解:
1. 線程安全與互斥鎖(std::mutex
)
int a = 0;
std::mutex mtx;
void func1()
{for (int i = 0; i < 1000; i++){mtx.lock(); // 加鎖a += 1;mtx.unlock(); // 解鎖}
}
線程安全問題:在多線程環境中,如果多個線程同時訪問和修改同一個共享變量(如這里的?a
),可能會導致數據競爭,產生不可預期的結果。
如果多個線程同時訪問同一個變量,并且其中至少有一個線程對該變量進行了寫操作,那么就會出現數據競爭問題。
?
數據競爭可能會導致程序崩潰、產生未定義的結果,或者得到錯誤的結果。
?
為了避免數據競爭問題,需要使用同步機制來確保多個線程之間對共享數據的訪問是安全的。常見的同步機制包括互斥量、條件變量、原子操作等。
std::mutex
:互斥鎖是一種同步原語,用于保護共享資源。
mtx.lock()
?會嘗試鎖定互斥鎖,如果鎖已經被其他線程持有,當前線程會被阻塞,直到鎖被釋放。
mtx.unlock()
?用于釋放鎖,允許其他線程獲取該鎖。
使用方式:在修改共享變量?a
?之前調用?mtx.lock()
?加鎖,修改完成后調用?mtx.unlock()
?解鎖,確保同一時間只有一個線程可以修改?a
,從而保證線程安全。
2. 互斥量死鎖
mutex m1, m2;
void func2()
{for (int i = 0; i < 50; i++){m1.lock();m2.lock();m1.unlock();m2.unlock();}
}void func3()
{for (int i = 0; i < 50; i++){m2.lock();m1.lock();m2.unlock();m1.unlock();}
}
死鎖原理:死鎖是指兩個或多個線程在執行過程中,因爭奪資源而造成的一種互相等待的現象。在?func2
?中,線程先鎖定?m1
?再鎖定?m2
;而在?func3
?中,線程先鎖定?m2
?再鎖定?m1
。如果兩個線程同時執行,可能會出現?func2
?持有?m1
?等待?m2
,而?func3
?持有?m2
?等待?m1
?的情況,從而導致死鎖。
解決方法:為了避免死鎖,所有線程應該按照相同的順序獲取鎖,例如都先獲取?m1
?再獲取?m2
。
3.?std::lock_guard
#include <iostream>
#include <thread>
#include <mutex>
#include <vector>std::mutex mtx3;
int b = 0;void fun5()
{for (int i = 0; i < 10000; i++){std::lock_guard<std::mutex> lg(mtx3);b++;}
}int main()
{const int numThreads = 5; // 定義線程數量std::vector<std::thread> threads;// 創建并啟動線程for (int i = 0; i < numThreads; i++){threads.emplace_back(fun5);}// 等待所有線程執行完畢for (auto& thread : threads){thread.join();}// 輸出最終結果std::cout << "Final value of b: " << b << std::endl;std::cout << "Expected value of b: " << 10000 * numThreads << std::endl;return 0;
}
作用:std::lock_guard
?是一個 RAII(資源獲取即初始化)風格的類模板,用于自動管理互斥鎖的加鎖和解鎖操作。
工作原理:當創建?std::lock_guard
?對象時,它會在構造函數中自動調用互斥鎖的?lock()
?方法加鎖;當?std::lock_guard
?對象離開其作用域時,它會在析構函數中自動調用互斥鎖的?unlock()
?方法解鎖。這樣可以避免手動調用?lock()
?和?unlock()
?可能導致的忘記解鎖問題,提高代碼的安全性。
4.?std::unique_lock
(1)示例
#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>
#include <vector>int c = 0;
std::timed_mutex mtx6;void func6() {for (int i = 0; i < 5000; i++) {std::unique_lock<std::timed_mutex> lg(mtx6, std::defer_lock);std::this_thread::sleep_for(std::chrono::seconds(5));if (lg.try_lock_for(std::chrono::seconds(5))) {c++;lg.unlock();}}
}int main() {const int numThreads = 3;std::vector<std::thread> threads;// 創建并啟動線程for (int i = 0; i < numThreads; i++) {threads.emplace_back(func6);}// 等待所有線程執行完畢for (auto& thread : threads) {thread.join();}// 輸出最終結果std::cout << "Final value of c: " << c << std::endl;std::cout << "Expected value of c: " << 5000 * numThreads << std::endl;return 0;
}
特點:std::unique_lock
?也是一個用于管理互斥鎖的 RAII 類,但它比?std::lock_guard
?更加靈活,包括延遲加鎖、條件變量、超時等。
std::unique_lock<std::timed_mutex> lg(mtx6, std::defer_lock);
創建一個?
std::unique_lock
?對象?lg
,用于管理?mtx6
?互斥鎖。參數?
defer_lock
:在創建?std::unique_lock
?對象時,傳遞?defer_lock
?參數表示不自動加鎖,需要手動調用?lock()
?或?try_lock()
?等方法來加鎖。
?
std::this_thread::sleep_for(std::chrono::seconds(5));
當前線程暫停執行 5 秒鐘。
?
lg.try_lock_for(std::chrono::seconds(5));
try_lock_for
:try_lock_for
?是?std::unique_lock
?提供的一個方法,用于嘗試在指定的時間內鎖定互斥鎖。代碼中的含義是:嘗試在 5 秒內鎖定互斥鎖。如果在 5 秒內成功鎖定,則返回?true
,否則返回?false
。
?(2)詳細知識點
1.靈活的鎖定策略
可以在創建?std::unique_lock
?對象時選擇是否立即鎖定互斥鎖。例如:
std::mutex mtx;
std::unique_lock<std::mutex> lock1(mtx); // 立即鎖定互斥鎖
std::unique_lock<std::mutex> lock2(mtx, std::defer_lock); // 不立即鎖定互斥鎖
2.?支持鎖的轉移
可以將一個?std::unique_lock
?對象的鎖所有權轉移給另一個?std::unique_lock
?對象。例如:
std::mutex mtx;
std::unique_lock<std::mutex> lock1(mtx);
std::unique_lock<std::mutex> lock2(std::move(lock1)); // 轉移鎖所有權
3.?支持帶超時的鎖定操作
如果使用的是?std::timed_mutex
?或?std::recursive_timed_mutex
,可以使用?try_lock_for
?和?try_lock_until
?方法進行帶超時的鎖定操作。例如:
std::timed_mutex mtx;
std::unique_lock<std::timed_mutex> lock(mtx, std::defer_lock);
if (lock.try_lock_for(std::chrono::seconds(2))) {// 成功鎖定互斥鎖
} else {// 鎖定超時
}
?
5. std::this_thread
std::this_thread
?是 C++ 標準庫中的一個命名空間,提供了與當前線程相關的一些實用函數。
(1)sleep_for
使當前線程暫停執行指定的時間段。例如:
std::this_thread::sleep_for(std::chrono::seconds(2)); // 線程暫停 2 秒
(2)sleep_until
使當前線程暫停執行直到指定的時間點。例如:
auto wake_time = std::chrono::steady_clock::now() + std::chrono::seconds(3);
std::this_thread::sleep_until(wake_time); // 線程暫停到指定時間點
(3)yield
當前線程放棄執行權,允許其他線程執行。例如:
std::this_thread::yield(); // 當前線程讓出 CPU 時間片
(4)get_id
返回當前線程的唯一標識符。例如:
std::thread::id this_id = std::this_thread::get_id();
std::cout << "Current thread ID: " << this_id << std::endl;
?
?