C++多線程中的互斥鎖
在多線程編程中,互斥鎖(mutex)是確保線程安全、避免數據競爭的重要工具。C++標準庫提供了多種互斥鎖,每種都有其特定的應用場景和特點。
主要有以下幾種互斥鎖(Mutex):
- std::mutex:最基本的互斥鎖,用于保護臨界區,確保同一時間只有一個線程可以訪問被保護的資源。
- std::timed_mutex:支持超時機制的互斥鎖,可以嘗試在給定時間內鎖定互斥鎖。如果在指定時間內沒有成功獲取鎖,則返回失敗。
- std::recursive_mutex:遞歸互斥鎖,同一線程可以多次獲取鎖而不會發生死鎖,通常用于遞歸函數中。
- std::recursive_timed_mutex:支持超時機制的遞歸互斥鎖,結合了遞歸鎖和超時鎖的特性。
- std::shared_mutex(C++17 引入):允許多個線程同時讀取,但只有一個線程可以寫入。適用于讀多寫少的場景。
- std::shared_timed_mutex(C++17 引入):支持超時機制的共享互斥鎖,可以在給定時間內嘗試獲取讀鎖或寫鎖。
這些是C++標準庫中提供的幾種主要的互斥鎖類型。每種鎖都有其特定的應用場景和使用方法,選擇合適的互斥鎖類型對于實現高效、安全的多線程程序非常重要。
一、基本互斥鎖(std::mutex)
std::mutex是最基本的互斥鎖,主要用于保護臨界區,確保同一時間只有一個線程可以訪問共享資源。
特點:
- 簡單易用,適用于大多數場景。
- 不能遞歸鎖定,同一線程多次嘗試鎖定會導致死鎖。
示例代碼:
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx;
void print_thread_id(int id) {
std::lock_guard<std::mutex> lock(mtx); // 自動管理鎖的獲取和釋放
std::cout << "Thread ID: " << id << std::endl;
}
int main() {
std::thread t1(print_thread_id, 1);
std::thread t2(print_thread_id, 2);
t1.join();
t2.join();
return 0;
}
二、帶超時機制的互斥鎖(std::timed_mutex)
std::timed_mutex在std::mutex的基礎上增加了超時功能,允許線程在指定時間內嘗試獲取鎖,如果在超時時間內未成功獲取鎖,則返回失敗。
特點:
- 適用于需要設置鎖獲取超時時間的場景。
- 提供try_lock_for和try_lock_until兩種超時嘗試獲取鎖的方法。
示例代碼:
#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>
std::timed_mutex tmtx;
void try_to_lock(int id) {
if(tmtx.try_lock_for(std::chrono::milliseconds(100))) {
std::cout << "Thread " << id << " locked the mutex" << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(200));
tmtx.unlock();
} else {
std::cout << "Thread " << id << " could not lock the mutex" << std::endl;
}
}
int main() {
std::thread t1(try_to_lock, 1);
std::thread t2(try_to_lock, 2);
t1.join();
t2.join();
return 0;
}
三、遞歸互斥鎖(std::recursive_mutex)
std::recursive_mutex允許同一線程多次獲取鎖而不會發生死鎖,這對于遞歸函數或需要多次鎖定的場景非常有用。
特點:
- 適用于遞歸調用和需要多次鎖定的場景。
- 需要注意避免濫用,因為遞歸鎖的使用會增加鎖定次數的復雜性。
示例代碼:
#include <iostream>
#include <thread>
#include <mutex>
std::recursive_mutex rmtx;
void recursive_function(int depth) {
rmtx.lock();
std::cout << "Depth: " << depth << std::endl;
if (depth > 0) {
recursive_function(depth - 1);
}
rmtx.unlock();
}
int main() {
std::thread t(recursive_function, 5);
t.join();
return 0;
}
四、帶超時機制的遞歸互斥鎖(std::recursive_timed_mutex)
std::recursive_timed_mutex結合了std::recursive_mutex和std::timed_mutex的特性,支持遞歸鎖定和超時機制。
特點:
- 適用于遞歸調用和需要超時機制的場景。
- 提供超時嘗試獲取遞歸鎖的方法。
示例代碼:
#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>
std::recursive_timed_mutex rtmmtx;
void try_recursive_lock(int id, int depth) {
if (rtmmtx.try_lock_for(std::chrono::milliseconds(100))) {
std::cout << "Thread " << id << " locked at depth " << depth << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(50));
if (depth > 0) {
try_recursive_lock(id, depth - 1);
}
rtmmtx.unlock();
} else {
std::cout << "Thread " << id << " could not lock at depth " << depth << std::endl;
}
}
int main() {
std::thread t1(try_recursive_lock, 1, 3);
std::thread t2(try_recursive_lock, 2, 3);
t1.join();
t2.join();
return 0;
}
五、共享互斥鎖(std::shared_mutex)
std::shared_mutex允許多個線程同時讀取,但只有一個線程可以寫入。這在讀多寫少的場景下非常有用。
特點:
- 適用于讀多寫少的場景。
- 讀操作和寫操作使用不同的鎖定機制。
示例代碼:
#include <iostream>
#include <thread>
#include <shared_mutex>
std::shared_mutex shmtx;
void read_shared(int id) {
std::shared_lock<std::shared_mutex> lock(shmtx); // 共享鎖
std::cout << "Thread " << id << " is reading" << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
void write_shared(int id) {
std::unique_lock<std::shared_mutex> lock(shmtx); // 獨占鎖
std::cout << "Thread " << id << " is writing" << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
int main() {
std::thread readers[5], writer(write_shared, 1);
for (int i = 0; i < 5; ++i) {
readers[i] = std::thread(read_shared, i + 2);
}
writer.join();
for (auto& reader : readers) {
reader.join();
}
return 0;
}
六、帶超時機制的共享互斥鎖(std::shared_timed_mutex)
std::shared_timed_mutex結合了std::shared_mutex和std::timed_mutex的特性,支持超時機制。
特點:
- 適用于讀多寫少且需要超時機制的場景。
- 提供超時嘗試獲取共享鎖的方法。
示例代碼:
#include <iostream>
#include <thread>
#include <shared_mutex>
#include <chrono>
std::shared_timed_mutex shtmmtx;
void try_read_shared(int id) {
if (shtmmtx.try_lock_shared_for(std::chrono::milliseconds(100))) {
std::cout << "Thread " << id << " is reading" << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(50));
shtmmtx.unlock_shared();
} else {
std::cout << "Thread " << id << " could not read" << std::endl;
}
}
void try_write_shared(int id) {
if (shtmmtx.try_lock_for(std::chrono::milliseconds(100))) {
std::cout << "Thread " << id << " is writing" << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(50));
shtmmtx.unlock();
} else {
std::cout << "Thread " << id << " could not write" << std::endl;
}
}
int main() {
std::thread readers[5], writer(try_write_shared, 1);
for (int i = 0; i < 5; ++i) {
readers[i] = std::thread(try_read_shared, i + 2);
}
writer.join();
for (auto& reader : readers) {
reader.join();
}
return 0;
}
總結
C++標準庫提供了多種類型的互斥鎖,每種鎖都有其特定的用途和特點。選擇合適的互斥鎖類型可以有效提高程序的并發性能和安全性。