首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

Effective Modern C++ 條款 35:任務導向程式設計優於執行緒導向程式設計

     編輯:關於C++

任務 vs 執行緒:兩種非同步執行策略

若需非同步執行函式 doAsyncWork(),有兩種基本選擇:

  1. 執行緒導向:建立 std::thread 直接執行。
  2. 任務導向:透過 std::async 提交任務。
// 執行緒導向
std::thread t(doAsyncWork);

// 任務導向
auto fut = std::async(doAsyncWork); // fut 為 future

任務導向的優勢

1. 程式碼更簡潔,自然取得結果與異常

  • 返回值:std::thread 無直接機制取得返回值;future::get() 輕鬆取得。
  • 異常傳播:doAsyncWork 拋出異常時,future::get() 會重新拋出;std::thread 會導致 std::terminate 終止程式。

2. 更高層級抽象,免除執行緒管理細節

C++ 中「執行緒」有三種含義:

  1. 硬體執行緒:CPU 核心支援的邏輯處理器。
  2. 軟體執行緒 (OS 執行緒):作業系統管理、排程至硬體執行緒的實體。
  3. std::thread:C++ 物件,內部操作軟體執行緒,可為空(預設建構、移動後、join 後、detach 後)。

3. 避免執行緒耗盡與過度訂閱

  • 執行緒耗盡:軟體執行緒為有限資源。std::thread 建構失敗會拋 std::system_error,即使函式標記 noexcept 也會拋出。處理策略複雜(當前執行緒同步執行?等待現有執行緒?)。
  • 過度訂閱:就緒軟體執行緒數 > 硬體執行緒數。導致上下文切換開銷、CPU 快取污染、效能下降。最佳比例隨負載動態變化,難以手動調校。

std::async 將這些問題轉交給標準庫實作者:

auto fut = std::async(doAsyncWork); // 執行緒管理責任轉移
  • 預設啟動策略不保證建立新執行緒,調度器可選擇在呼叫 get()/wait() 的執行緒上同步執行,自然避免過度訂閱。
  • 先進調度器採用系統級執行緒池 + 工作竊取演算法,自動平衡負載、最大化硬體利用率。
  • 標準庫未強制要求執行緒池,但主流實作(MSVC、libstdc++、libc++)均已採用。使用任務導向,未來庫升級自動受惠。

4. 負載平衡與 GUI 回應性考量

同步執行(呼叫端執行緒直接跑任務)可能造成負載不均。std::async 讓調度器決策。若 GUI 執行緒需嚴格回應性,可指定啟動策略:

auto fut = std::async(std::launch::async, doAsyncWork); // 強制新執行緒

何時仍需 std::thread?

任務導向覆蓋 90%+ 場景,僅以下情況適合直接用 std::thread:

  1. 需存取平台原生執行緒 API:std::thread::native_handle() 提供底層句柄(如設定優先權、親和性),std::future 無對應功能。
  2. 特定平台單一程式深度優化:固定硬體部署的高效能服務,可手動調校執行緒數量與親和性。
  3. 實作執行緒基礎設施:建構執行緒池、Actor 框架等 C++ 標準庫未提供的高層抽象。

總結

  1. std::thread 無法直接取得返回值/異常,導致程式終止風險。
  2. 執行緒導向需手動處理:耗盡、過度訂閱、負載平衡、跨平台適配。
  3. 預設 std::async 任務導向解決上述大多數問題,享受標準庫演進紅利。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved