第16天:C++多線程完全指南 - 從基礎到現代并發編程
一、多線程基礎概念
1. 線程創建與管理(C++11)
#include <iostream>
#include <thread>void hello() {std::cout << "Hello from thread " << std::this_thread::get_id() << "\n";
}int main() {std::thread t1(hello);std::thread t2([](){std::cout << "Lambda thread running\n";});t1.join(); // 等待線程完成t2.join();// 輸出可能交錯:// Hello from thread 140245230233344// Lambda thread running
}
2. 并發與并行區別
- 并發:交替處理多個任務(單核)
- 并行:同時處理多個任務(多核)
// 查看硬件支持線程數
unsigned int n = std::thread::hardware_concurrency();
std::cout << n << " concurrent threads supported\n";
二、線程同步核心機制
1. 互斥鎖(mutex)與RAII
#include <mutex>std::mutex mtx;
int shared_data = 0;void safe_increment() {std::lock_guard<std::mutex> lock(mtx); // 自動釋放鎖++shared_data; // 臨界區操作
}int main() {std::thread threads[10];for (auto& t : threads) {t = std::thread(safe_increment);}for (auto& t : threads) {t.join();}std::cout << "Final value: " << shared_data; // 正確輸出10
}
2. 條件變量(生產者-消費者模式)
#include <queue>
#include <condition_variable>std::mutex mtx;
std::condition_variable cv;
std::queue<int> data_queue;void producer() {for (int i=0; i<5; ++i) {{std::lock_guard<std::mutex> lock(mtx);data_queue.push(i);}cv.notify_one(); // 通知消費者std::this_thread::sleep_for(std::chrono::milliseconds(100));}
}void consumer() {while(true) {std::unique_lock<std::mutex> lock(mtx);cv.wait(lock, []{ return !data_queue.empty(); });int data = data_queue.front();data_queue.pop();lock.unlock();std::cout << "Consumed: " << data << "\n";if(data == 4) break;}
}
三、現代C++并發特性
1. 異步任務(std::async)
#include <future>int compute(int x) {return x * x;
}int main() {auto future = std::async(std::launch::async, compute, 12);std::cout << "Result: " << future.get(); // 輸出144
}
2. 原子操作(std::atomic)
#include <atomic>std::atomic<int> counter(0); // 無需鎖的線程安全計數器void increment() {for (int i=0; i<100000; ++i) {++counter; // 原子操作}
}// 測試:兩個線程同時遞增
// 最終結果正確為200000
四、線程安全設計模式
1. 線程局部存儲(thread_local)
thread_local int tls_var = 0; // 每個線程獨立副本void thread_func() {++tls_var;std::cout << "Thread " << std::this_thread::get_id() << ": " << tls_var << "\n";
}// 每個線程輸出自己的遞增結果
2. 線程池實現(C++17)
#include <vector>
#include <functional>
#include <queue>class ThreadPool {std::vector<std::thread> workers;std::queue<std::function<void()>> tasks;std::mutex queue_mutex;std::condition_variable condition;bool stop = false;public:ThreadPool(size_t threads) {for(size_t i=0; i<threads; ++i) {workers.emplace_back([this]{while(true) {std::function<void()> task;{std::unique_lock<std::mutex> lock(queue_mutex);condition.wait(lock, [this]{ return stop || !tasks.empty(); });if(stop && tasks.empty()) return;task = std::move(tasks.front());tasks.pop();}task();}});}}template<class F>void enqueue(F&& f) {{std::lock_guard<std::mutex> lock(queue_mutex);tasks.emplace(std::forward<F>(f));}condition.notify_one();}~ThreadPool() {{std::lock_guard<std::mutex> lock(queue_mutex);stop = true;}condition.notify_all();for(auto& worker : workers)worker.join();}
};
五、并發編程陷阱與調試
1. 死鎖檢測示例
std::mutex m1, m2;void thread_A() {std::lock_guard<std::mutex> lock1(m1);std::this_thread::sleep_for(std::chrono::milliseconds(100));std::lock_guard<std::mutex> lock2(m2); // 可能死鎖點
}void thread_B() {std::lock_guard<std::mutex> lock2(m2);std::this_thread::sleep_for(std::chrono::milliseconds(100));std::lock_guard<std::mutex> lock1(m1); // 可能死鎖點
}// 解決方案:使用std::lock同時鎖定多個互斥量
void safe_lock() {std::lock(m1, m2);std::lock_guard<std::mutex> lock1(m1, std::adopt_lock);std::lock_guard<std::mutex> lock2(m2, std::adopt_lock);
}
六、現代C++并發增強
1. C++20信號量(semaphore)
#include <semaphore>std::counting_semaphore<5> sem(3); // 允許3個同時訪問void limited_thread() {sem.acquire();// 訪問受限資源sem.release();
}
2. 屏障(C++20 barrier)
std::barrier sync_point(3); // 等待3個線程到達void worker() {// Phase 1sync_point.arrive_and_wait();// Phase 2(所有線程完成Phase1后繼續)
}
七、常見問題解答
Q:如何檢測數據競爭?
- 使用ThreadSanitizer編譯:
g++ -fsanitize=thread -g -O1 program.cpp
- 示例輸出:
WARNING: ThreadSanitizer: data race
Q:std::mutex和std::shared_mutex區別?
- std::mutex:獨占鎖
- std::shared_mutex:讀寫鎖(C++17)
std::shared_mutex smtx;// 寫操作使用獨占鎖
{std::unique_lock lock(smtx);data = new_value;
}// 讀操作使用共享鎖
{std::shared_lock lock(smtx);read_data = data;
}
八、今日總結
? 核心掌握:
- 🧵 線程生命周期管理(創建、等待、分離)
- 🔒 同步原語使用場景(mutex/atomic/condition_variable)
- 🚧 典型并發問題檢測與預防(死鎖、數據競爭)