《C++ 108好庫》之之2 多線程庫thread,mutex,condition_variable,this_thread
- 《C++ 108好庫》之2 多線程庫thread,mutex,condition_variable,this_thread
- std::thread類
- ??互斥量(Mutex)??類
- std::mutex??類
- std::lock_guard類
- std::unique_lock類
- ??條件變量(Condition Variable)??類
- 獲取線程ID和 延時
- 使用例子
- 創建線程
- 使用mutex和Condition Variable的生產者-消費者模型
《C++ 108好庫》之2 多線程庫thread,mutex,condition_variable,this_thread
C++11 中,多線程編程通過標準庫 正式引入,提供了跨平臺的線程管理能力。
多線程一般由這些類合作構成:std::thread類、std::mutex??類、std::lock_guard類、 std::unique_lock類、condition_variable類
std::thread類
1 ??創建線程??
explicit thread(_Callable&& __f, _Args&&… __args)
使用 std::thread類創建線程,構造函數接受函數對象及參數:
向線程函數傳遞參數時,參數會被復制到線程的獨立內存中;如果希望傳遞引用,需要使用std::ref。
2??線程管理??:
join():等待線程完成。
detach():將線程與主線程分離,使其獨立運行。一旦分離,就不能再與之通信。
注意:在銷毀std::thread對象之前,必須調用join()或detach(),否則程序會終止(調用std::terminate)。
??互斥量(Mutex)??類
C++11提供了std::mutex用于保護共享數據,防止數據競爭。
std::mutex??類
std::mutex()
std::mutex::lock() :調用此函數會嘗試??鎖定??互斥體。
行為:??如果互斥體當前??未被任何線程鎖定??,則調用線程獲得鎖,函數立即返回。如果互斥體??已被其他線程鎖定??,則調用線程會被??阻塞??(掛起),直到持有鎖的線程解鎖該互斥體。一旦解鎖,等待的線程之一(取決于調度)會獲得鎖并繼續執行。
??特點:?? 阻塞調用。如果忘記解鎖會導致死鎖。
??用法:?? 當你需要確保臨界區被獨占訪問,并且可以接受線程阻塞等待時使用。
std::mutex::try_lock(): 調用此函數會??嘗試??鎖定互斥體,但??不會阻塞??調用線程。
??行為:??如果互斥體當前??未被鎖定??,則調用線程獲得鎖,函數返回 true。如果互斥體??已被鎖定??(無論是當前線程還是其他線程),則函數??立即返回 false??,線程繼續執行而不會被阻塞。
??特點:?? 非阻塞調用。需要檢查返回值來判斷是否成功獲得鎖。
??用法:?? 當你需要在鎖不可用時做其他事情,避免阻塞線程時使用。常用于輪詢、避免死鎖策略(如按固定順序獲取多個鎖失敗時釋放已持有的鎖)或非關鍵路徑的優化。
std::mutex::unlock():解鎖
std::lock_guard類
它在構造時自動鎖定給定的互斥體,并在析構時(通常是離開作用域時)自動解鎖它。
??構造時:?? 調用傳入的互斥體(如 std::mutex)的 lock()方法。這會阻塞直到獲得鎖。
??析構時:?? 調用互斥體的 unlock()方法。
用法:std::lock_guardstd::mutex lock(mtx); // 構造時自動調用 mtx.lock()
std::unique_lock類
功能比 lock_guard??更強大、更靈活??。它也管理互斥體的鎖定和解鎖,但提供了更多的控制選項。
??行為/特點:??
??靈活的構造:??默認構造:創建一個不關聯任何互斥體的 unique_lock。
關聯互斥體并立即鎖定(阻塞):unique_lock lock(mtx);(等價于 lock_guard)
關聯互斥體但??延遲鎖定??:unique_lock lock(mtx, std::defer_lock);構造時不鎖定,稍后手動調用 lock(), try_lock()或 try_lock_for()/try_lock_until()(針對 std::timed_mutex)。
關聯互斥體并嘗試鎖定:unique_lock lock(mtx, std::try_to_lock);構造時調用 mtx.try_lock()。
關聯互斥體并假設已鎖定(接管所有權):unique_lock lock(mtx, std::adopt_lock);假設調用線程已經持有 mtx的鎖,unique_lock負責后續解鎖。
手動控制:??
lock(): 阻塞鎖定關聯的互斥體(如果尚未鎖定)。
try_lock(): 嘗試非阻塞鎖定關聯的互斥體。
unlock(): ??手動解鎖??關聯的互斥體,在 unique_lock析構前可以臨時釋放鎖(這在 lock_guard中是不可能的)。析構時如果互斥體仍被該 unique_lock持有,會自動解鎖。
release(): 釋放對互斥體的所有權管理(返回互斥體指針),不再負責解鎖。調用者需手動管理。
??所有權:??
可移動 (std::move),但不可復制。鎖的所有權可以在 unique_lock對象之間轉移。
??條件變量:?? std::condition_variable::wait等函數??必須??使用 std::unique_lockstd::mutex作為參數,因為它需要能在等待時解鎖和重新鎖定互斥體(這是 lock_guard做不到的)。
??條件變量(Condition Variable)??類
<condition_variable>
用于線程間的同步,允許線程在某些條件不滿足時休眠,直到被其他線程通知。通常與互斥量一起使用。
void notify_one() noexcept;
void notify_all() noexcept;
wait(unique_lock& __lock) noexcept;
wait_until(unique_lock& __lock,const chrono::time_point<__clock_t, _Duration>& __atime)
wait_for(unique_lock& __lock, const chrono::duration<_Rep, _Period>& __rtime)
獲取線程ID和 延時
std::this_thread里的函數
inline thread::id get_id()
獲取??當前執行線程的唯一標識符??。
std::thread::id main_id = std::this_thread::get_id(); // 輸出示例:0x70000a15c000
inline void sleep_for(const chrono::duration<_Rep, _Period>& __rtime)
使??當前線程阻塞??一段指定的??相對時間??。
std::this_thread::sleep_for(std::chrono::seconds(1));
inline void sleep_until(const chrono::time_point<_Clock, _Duration>& __atime)
使??當前線程阻塞??直到??絕對時間點??。
使用例子
創建線程
std::thread thread_hello(hello,"LiHua");
thread_hello.join();
傳入函數給線程
void hello(const std::string str) {
std::cout << "hello world! "<<str<<std::endl;
}
使用mutex和Condition Variable的生產者-消費者模型
std::thread thread_producer(producer);
thread_producer.detach();std::thread thread_consumer(consumer);
thread_consumer.join();
其中的線程函數
std::queue<int> dataQueue;
std::mutex mtx_test_thread;
std::condition_variable cv_test_thread;
void producer() {for (int i = 0; i < 50; ++i) {{std::lock_guard<std::mutex> lock(mtx_test_thread);dataQueue.push(i); std::cout << "push:" << i << std::endl;}std::this_thread::sleep_for(std::chrono::seconds(1));cv_test_thread.notify_one(); // 通知消費者}
}void consumer() {while (true) {std::unique_lock<std::mutex> lock(mtx_test_thread);cv_test_thread.wait(lock, [] { return !dataQueue.empty(); });int value = dataQueue.front();dataQueue.pop();lock.unlock();if (value == -1) break; // 終止條件std::cout << "Consumed: " << value << std::endl;}
}