線程同步保證了多個線程在并發(fā)執(zhí)行時的正確性,而線程通信則是為了使多個線程之間能夠相互傳遞信息。在C++中,我們可以使用不同的機制來實現(xiàn)線程同步和通信,主要包括互斥量(mutex)、條件變量(condition_variable)、讀寫鎖(shared_mutex)、信號量(semaphore)等。小編將詳細介紹這些常見的同步和通信機制,并討論其使用場景。
一、線程同步的常見方法
1. 互斥量(mutex)
互斥量(mutex) 是最常用的線程同步機制,它用于保證同一時刻只有一個線程能夠訪問共享資源。通過互斥量,可以避免多個線程同時訪問共享資源導(dǎo)致的數(shù)據(jù)競爭和不一致性問題。
使用方法: C++標(biāo)準(zhǔn)庫提供了 std::mutex 類來實現(xiàn)互斥量,并通過 std::lock_guard 或 std::unique_lock 來自動管理鎖的加鎖與解鎖。
示例代碼:
cppCopy Code#include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx; // 定義互斥量
void print_number(int n) {
std::lock_guard<std::mutex> lock(mtx); // 自動加鎖
std::cout << "Number: " << n << std::endl;
}
int main() {
std::thread t1(print_number, 1);
std::thread t2(print_number, 2);
t1.join();
t2.join();
return 0;
}
解釋:
std::mutex 用于保護共享資源。在這里是 std::cout。
std::lock_guard 是一個RAII類型的類,它在作用域內(nèi)自動加鎖并在作用域結(jié)束時自動解鎖,避免了手動解鎖可能引發(fā)的錯誤。
2. 讀寫鎖(shared_mutex)
C++17引入了 std::shared_mutex,用于實現(xiàn)讀寫鎖機制。它允許多個線程同時讀取共享資源,但在寫線程占用資源時,其他線程不能同時讀或?qū)憽?/p>
使用方法:
std::shared_lock 用于讀操作,允許多個線程并行讀取。
std::unique_lock 用于寫操作,保證寫操作時沒有其他線程訪問該資源。
示例代碼:
cppCopy Code#include <iostream>
#include <thread>
#include <shared_mutex>
std::shared_mutex rw_mutex; // 定義讀寫鎖
int shared_data = 0;
void reader() {
std::shared_lock<std::shared_mutex> lock(rw_mutex); // 讀取時共享鎖
std::cout << "Reader thread reads: " << shared_data << std::endl;
}
void writer(int value) {
std::unique_lock<std::shared_mutex> lock(rw_mutex); // 寫入時獨占鎖
shared_data = value;
std::cout << "Writer thread writes: " << value << std::endl;
}
int main() {
std::thread t1(reader);
std::thread t2(writer, 42);
std::thread t3(reader);
t1.join();
t2.join();
t3.join();
return 0;
}
解釋:
讀線程通過 std::shared_lock 進行共享鎖定,允許多個線程并行讀取。
寫線程通過 std::unique_lock 獨占鎖定,保證數(shù)據(jù)一致性。
3. 條件變量(condition_variable)
條件變量是用于線程間同步的機制,允許一個線程等待某個條件滿足,或者通知其他線程某個條件已經(jīng)發(fā)生改變。條件變量通常與互斥量一起使用。
使用方法:
std::condition_variable 用于線程等待和通知。
std::mutex 用于保護共享資源。
示例代碼:
cppCopy Code#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
void print_id(int id) {
std::unique_lock<std::mutex> lock(mtx);
while (!ready) cv.wait(lock); // 等待條件變量通知
std::cout << "Thread " << id << std::endl;
}
void go() {
std::unique_lock<std::mutex> lock(mtx);
ready = true;
cv.notify_all(); // 通知所有等待的線程
}
int main() {
std::thread threads[10];
for (int i = 0; i < 10; ++i) {
threads[i] = std::thread(print_id, i);
}
std::cout << "Waiting..." << std::endl;
go(); // 改變條件,通知線程開始執(zhí)行
for (auto& th : threads) {
th.join();
}
return 0;
}
解釋:
cv.wait(lock) 會使線程阻塞,直到條件滿足。
cv.notify_all() 通知所有等待的線程繼續(xù)執(zhí)行。
二、線程通信的常見方法
1. 使用共享數(shù)據(jù)結(jié)構(gòu)
線程間可以通過共享數(shù)據(jù)結(jié)構(gòu)來進行通信。例如,一個線程可以將結(jié)果寫入共享隊列,另一個線程從隊列中讀取數(shù)據(jù)。這種方式非常適用于生產(chǎn)者-消費者模型。
示例代碼(使用 std::queue 和 std::mutex):
cppCopy Code#include <iostream>
#include <thread>
#include <queue>
#include <mutex>
#include <condition_variable>
std::queue<int> data_queue;
std::mutex mtx;
std::condition_variable cv;
void producer() {
for (int i = 0; i < 5; ++i) {
std::lock_guard<std::mutex> lock(mtx);
data_queue.push(i);
std::cout << "Produced: " << i << std::endl;
cv.notify_all(); // 通知消費者有數(shù)據(jù)可用
}
}
void consumer() {
while (true) {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return !data_queue.empty(); }); // 等待數(shù)據(jù)
int value = data_queue.front();
data_queue.pop();
std::cout << "Consumed: " << value << std::endl;
if (value == 4) break; // 消費到一定數(shù)據(jù)后退出
}
}
int main() {
std::thread prod(producer);
std::thread cons(consumer);
prod.join();
cons.join();
return 0;
}
解釋:
生產(chǎn)者線程將數(shù)據(jù)放入 data_queue 隊列中,并通知消費者線程。
消費者線程從隊列中取出數(shù)據(jù)進行處理。
2. 信號量(semaphore)
信號量是一種計數(shù)同步機制,用于控制對共享資源的訪問。C++20引入了 std::counting_semaphore,可以用來控制多個線程對資源的訪問。
使用方法:
std::counting_semaphore 用于管理線程訪問的許可數(shù)量。
示例代碼:
cppCopy Code#include <iostream>
#include <thread>
#include <semaphore>
std::counting_semaphore<3> sem(3); // 最多允許3個線程同時執(zhí)行
void access_resource(int id) {
sem.acquire(); // 獲取許可
std::cout << "Thread " << id << " is accessing the resource." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
sem.release(); // 釋放許可
}
int main() {
std::thread t1(access_resource, 1);
std::thread t2(access_resource, 2);
std::thread t3(access_resource, 3);
std::thread t4(access_resource, 4);
t1.join();
t2.join();
t3.join();
t4.join();
return 0;
}
解釋:
std::counting_semaphore 控制允許同時訪問共享資源的線程數(shù)量。
acquire 獲取許可,release 釋放許可。
C++提供了多種線程同步和通信機制來幫助開發(fā)者在多線程編程中保持?jǐn)?shù)據(jù)一致性與安全性。常用的線程同步方法包括 互斥量、讀寫鎖 和 條件變量,它們可以幫助我們解決線程之間的競爭問題。而線程通信則通常使用共享數(shù)據(jù)結(jié)構(gòu)、信號量等機制,使得線程之間可以安全地傳遞數(shù)據(jù)。掌握這些方法并靈活應(yīng)用,能夠有效提高多線程程序的性能和穩(wěn)定性。