在共享記憶體中進行執行緒間的同步是確保多執行緒程式正確執行的關鍵,以下是幾種常見的方法:
1. 使用互斥鎖(Mutex)
-
原理:
- 互斥鎖用於保護共享資源,確保在同一時刻只有一個執行緒能夠訪問被鎖定的共享記憶體區域。當一個執行緒想要訪問共享記憶體時,它首先嚐試獲取互斥鎖。如果鎖已經被其他執行緒持有,那麼該執行緒將被阻塞,直到鎖被釋放。一旦獲取到鎖,執行緒就可以安全地訪問共享記憶體,完成操作後再釋放鎖,以便其他執行緒能夠獲取。
-
示例程式碼(以C++為例):
#include <iostream>
#include <thread>
#include <mutex>
// 定義互斥鎖
std::mutex mutex_shared_memory;
// 共享記憶體結構體
struct SharedMemory {
int data;
};
// 執行緒函式,用於修改共享記憶體中的資料
void thread_function(SharedMemory* shared_memory) {
// 獲取互斥鎖
std::unique_lock<std::mutex> lock(mutex_shared_memory);
// 修改共享記憶體中的資料
shared_memory->data += 1;
// 釋放互斥鎖
lock.unlock();
}
int main() {
// 建立共享記憶體物件
SharedMemory shared_memory = {0};
// 建立多個執行緒
std::thread threads[5];
for (int i = 0; i < 5; i++) {
threads[i] = std::thread(thread_function, &shared_memory);
}
// 等待所有執行緒完成
for (int i = 0; i < 5; i++) {
threads[i].join();
}
// 輸出共享記憶體中的資料
std::cout << "共享記憶體中的資料: " << shared_memory.data << std::endl;
return 0;
}
- 在上述程式碼中,首先定義了一個互斥鎖`mutex_shared_memory`,然後在`thread_function`執行緒函式中,當要訪問共享記憶體結構體`SharedMemory`中的資料時,先透過`std::unique_lock`獲取互斥鎖,完成資料修改後再釋放鎖。這樣就保證了在同一時刻只有一個執行緒能夠修改共享記憶體中的資料。
2. 使用訊號量(Semaphore)
-
原理:
- 訊號量是一種用於控制多個執行緒對共享資源訪問的同步機制,它維護了一個計數器。執行緒在訪問共享資源之前需要先獲取訊號量,如果訊號量的值大於零,執行緒可以獲取訊號量並將其值減一,然後訪問共享資源;如果訊號量的值為零,執行緒將被阻塞,直到訊號量的值大於零。當執行緒完成對共享資源的訪問後,會釋放訊號量,即將訊號量的值加一,以便其他執行緒能夠獲取。
-
示例程式碼(以C++為例):
#include <iostream>
#include <thread>
#include <semaphore.h>
// 定義訊號量
sem_t semaphore_shared_memory;
// 共享記憶體結構體
struct SharedMemory {
int data;
};
// 執行緒函式,用於修改共享記憶體中的資料
void thread_function(SharedMemory* shared_memory) {
// 獲取訊號量
sem_wait(&semaphore_shared_memory);
// 修改共享記憶體中的資料
shared_memory->data += 1;
// 釋放訊號量
sem_post(&semaphore_shared_memory);
}
int main() {
// 初始化訊號量,設定初始值為1
sem_init(&semaphore_shared_memory, 0, 1);
// 建立共享記憶體物件
SharedMemory shared_memory = {0};
// 建立多個執行緒
std::thread threads[5];
for (int i = 0; i < 5; i++) {
threads[i] = std::thread(thread_function, &shared_memory);
}
// 等待所有執行緒完成
for (int i = 0; i < 5; i++) {
threads[i].join();
}
// 輸出共享記憶體中的資料
std::cout << "共享記憶體中的資料: " << shared_memory.data << std::endl;
// 銷燬訊號量
sem_destroy(&semaphore_shared_memory);
return 0;
}
- 在上述程式碼中,首先定義了一個訊號量`semaphore_shared_memory`,並透過`sem_init`函式將其初始化為1。在`thread_function`執行緒函式中,執行緒在訪問共享記憶體結構體`SharedMemory`中的資料之前,先透過`sem_wait`獲取訊號量,完成資料修改後再透過`sem_post`釋放訊號量。這樣就保證了對共享記憶體的有序訪問。
3. 使用條件變數(Condition Variable)
-
原理:
- 條件變數通常與互斥鎖一起使用,用於讓執行緒在滿足特定條件時進行等待或被喚醒。執行緒首先獲取互斥鎖,然後檢查共享記憶體中的某個條件是否滿足,如果不滿足則釋放互斥鎖並等待條件變數的通知;當其他執行緒修改共享記憶體使得條件滿足時,會傳送條件變數的通知,等待的執行緒收到通知後會重新獲取互斥鎖並繼續執行後續操作。
-
示例程式碼(以C++為例):
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
// 定義互斥鎖和條件變數
std::mutex mutex_shared_memory;
std::condition_variable condition_variable_shared_memory;
// 共享記憶體結構體
struct SharedMemory {
int data;
};
// 執行緒函式,用於修改共享記憶體中的資料並根據條件喚醒其他執行緒
void thread_function(SharedMemory* shared_memory) {
// 獲取互斥鎖
std::unique_lock<std::mutex> lock(mutex_shared_memory);
// 修改共享記憶體中的資料
shared_memory->data += 1;
// 如果資料達到某個值,喚醒其他等待的執行緒
if (shared_memory->data >= 3) {
condition_variable_shared_memory.notify_all();
}
// 釋放互斥鎖
lock.unlock();
}
// 執行緒函式,用於等待條件滿足
void wait_function(SharedMemory* shared_memory) {
// 獲取互斥鎖
std::unique_lock<std::mutex> lock(mux_shared_memory);
// 等待條件變數的通知,直到資料達到某個值
condition_variable_shared_memory.wait(lock, [&shared_memory]() {
return shared_memory->data >= 3;
});
// 輸出共享記憶體中的資料
std::cout << "共享記憶體中的資料達到了要求的值: " << shared_memory->data << std::endl;
// 釋放互斥鎖
lock.unlock();
}
int main() {
// 建立共享記憶體物件
SharedMemory shared_memory = {0};
// 建立多個執行緒
std::thread threads[5];
for (int i = 0; i < 5; i++) {
if (i < 3) {
threads[i] = std::thread(thread_function, &shared_memory);
} else {
threads[i] = std::thread(wait_function, &shared_memory);
}
}
// 等待所有執行緒完成
for (int i = 0; i < 5; i++) {
threads[i].join();
}
// 輸出共享記憶體中的資料
std::cout << "共享記憶體中的資料: " << shared_memory.data << std::endl;
return 0;
}
- 在上述程式碼中,定義了互斥鎖`mutex_shared_memory`和條件變數`condition_variable_shared_memory`。在`thread_function`執行緒函式中,修改共享記憶體中的資料後,如果資料達到某個值(這裡是3),就透過`notify_all`通知其他等待的執行緒。在`wait_function`執行緒函式中,先獲取互斥鎖,然後透過`wait`等待條件變數的通知,直到共享記憶體中的資料達到要求的值,然後輸出資料並釋放互斥鎖。這樣就實現了根據共享記憶體中的條件進行執行緒間的同步。
以上就是在共享記憶體中進行執行緒間同步的幾種常見方法,每種方法都有其特點和適用場景,在實際應用中可以根據具體情況選擇合適的同步機制。