1. 考量重點
2. 效能評估
3. Lambda Expression
4. std::function()
5. Race Condition
6. std::lock_guard 的特性
7. std::unique_lock 的特性
8. Deadlock 範例
9. Condition Variable
10. Recursive Mutex 和 Reentrant Locking
Parallelization 的第一步是找到可以獨立執行的工作,讓每個 Thread 處理自己的資料範圍,甚至不需要共享可變動的資料,就能大幅降低 Synchronization 的需求。
成本: 儘管不需要 mutex 和 lock,仍有可能以下成本:
Amdahl’s Law 平行化的理論限制。假設一個程式有90%的工作可以平行化,另外10%必須依序執行,即使無限多個 Thread,仍然無法消除那10%的 Sequential Work。
公式: Speedup = 1 / ((1-P) + P/T),其中 P 為可以平行化的比例,T 為 Thread 的數量
Speedup = 1 / (0.1 + 0.9/4) ≒ 3.08
Lambda 可以理解為一種匿名函式 (Anonymous Function),不需要事先宣告名稱,就能直接建立一個可呼叫的物件。
形式: [Capture List] (Parameter List) { // Function Body }
可以用來保存不同種類的 Callable Object:
效能問題: 4個 Thread 各自找到1000萬筆符合條件的 Rectangle。
不同 Thread 使用相反的鎖定順序:
std::mutex geometry_mutex;
std::mutex connectivity_mutex;
void update_geometry() {
std::lock_guard<std::mutex> lock1(geometry_mutex);
std::lock_guard<std::mutex> lock2(connectivity_mutex);
…
}
void update_connectivity() {
std::lock_guard<std::mutex> lock2(connectivity_mutex);
std::lock_guard<std::mutex> lock1(geometry_mutex);
…
}Thread A: 持有 Geometry、等待 Connectivity
Thread B: 持有 Connectivity、等待 Geometry
優點:
如果其他地方有不同的同步依賴,仍然可能 Deadlock。
常見操作:
範例:
void producer(int task) {
{
std::lock_guard<std::mutex> lock(queue_mutex);
task_queue.push(task);
}
cv.notify_one();
}
void worker() {
{
std::unique_lock<std::mutex> lock(queue_mutex);
cv.wait(lock, [] { // Predicate避免Spurious Wakeup
return !task_queu.empty();
});
task = task_queue.front();
task_queue.pop();
}
process_task(task)
}Self-Deadlock 範例:
std::mutex mtx;
void update_geometry() {
std::lock_guard<std::mutex> lock(mtx);
…
}
void rebuild_layout() {
std::lock_guard<std::mutex> lock(mtx);
…
update_geometry();
}同一個 Thread 已持有 Mutex,又試圖取得同一把不可遞迴的 Mutex。
std::recursive_mutex 允許同一個 Thread 重複取得同一把 Mutex:
Last updated: