異步編程中,回調地獄讓代碼難以維護。C++11/Boost 引入 future/promise 機制,將異步操作轉為同步風格,代碼清晰、易組合。本文從組件依賴、核心類設計、共享狀態機制、then 鏈式調用實現、when_all 並行併發五個維度,深度剖析基於 Boost 實現的 future/promise 內部原理,附關鍵源碼邏輯解讀。
+------------------+ | bind / function | (回調綁定與類型擦除) +------------------+ | shared_ptr / | (引用計數 / 作用域指針 / 元組 / 異常) | scoped_ptr | | tuple | | exception | +------------------+ | Future / Promise | (核心異步原語) +------------------+ | when_all | (並行任務聚合) +------------------+
enable_shared_from_this 模式當對象由 shared_ptr 管理,且成員函數需將 指向自身的 shared_ptr 傳遞給外部時,必須繼承 enable_shared_from_this 並調用 shared_from_this()。
class TcpConnection : public enable_shared_from_this {
void start() {
async_write(socket_, buffer_,
bind(&TcpConnection::handle_write, shared_from_this(), _1, _2));
// 若傳 this,外部構造 shared_ptr 會導致雙重析構!
}
};
原因:直接傳 this 構造 shared_ptr 會創建**獨立控制塊**,導致雙重刪除。shared_from_this() 返回與原 shared_ptr 共享控制塊的新 shared_ptr。
tuple 多返回值匿名聚合多個值,避免定義專用 struct,std::pair 是 2 元組特例。
FutureBase(非特化通用邏輯) ↑ Future (具體接口:get/then/isDone...) Promise (提供 setValue/setException) │ └─ m_future → SharedPtr > ←──┐ │ 共享同一個 Future │ FutureObject │ │ └─ m_future ────────────────────────────────────────┘
FutureObjectInterface 兩種實現| 實現類 | 適用場景 | 特點 |
|---|---|---|
FutureObject | 與 Promise 配對(異步產生值) | **線程安全**:內部鎖保護,支援多線程 set/get、回調註冊、狀態變更通知 |
PromptFutureObject | 直接用值構造 Future(如 makeFuture) | **無鎖只讀**:值構造時已就緒,無併發寫入,性能更優 |
setValue(T) // 設置值(僅能調用一次,否則拋異常)
setException(E) // 設置異常
getFuture() // 返回綁定同一共享狀態的 Future
get() // 阻塞等待就緒,返回值或拋異常
isDone() / hasValue() / hasException()
then(callback) // 註冊回調,返回新 Future(鏈式調用核心)
template
Future::type>
then(const Callback&)>& callback);
resolved_type 特化:resolved_type::type = T resolved_type>::type = U U 還是 Future,then 統一返回 Future,保證鏈式調用類型一致。
Future then(Callback) {
// 1. 檢查 m_future 已初始化
// 2. 創建新 Promise promise; // 承載回調返回值
// 3. 將 callback 包裝為 SequentialCallback
// SequentialCallback 持有:原 callback + 新 promise
// 4. 註冊到共享狀態:m_future->registerCallback(wrapped)
// 5. 返回 promise.getFuture()
}
SequentialCallback 三種運行模式| 回調返回類型 | 處理邏輯 |
|---|---|
void | 調用 callback,調用 promise.set()(無值) |
非 Future 值 U | 調用 callback 得 u,調用 promise.setValue(u) |
Future | 調用 callback 得 fut,對 fut 再次 then 綁定 ForwardValue,最終將值轉發給外層 promise |
關鍵點:回調返回 Future 時,通過**遞歸 then + ForwardValue** 將內層值「剝離」傳遞給外層 promise,實現扁平化鏈式調用。
利用 enable_if + is_void + is_future_type (traits) 在編譯期分發三種 run 重載,零運行時開銷。
// 目標:同時啟動多個異步任務,全部完成後聚合結果
Future> when_all(Future f1, Future f2, ...);
// 實現思路:
// 1. 創建一個共享計數器(原子計數 / 條件變量)
// 2. 對每個 fi 註冊回調:完成時計數器--,存儲結果到 tuple 對應位置
// 3. 當計數器歸零 → 觸發聚合 Future 就緒
// 4. 利用 tuple 存儲異構結果,類型安全
完整實現涉及 tuple 索引序列、原子操作、條件變量通知,篇幅限制此處略過,後續補充。
// 業務:A → B(異步) → C(異步) → 返回客戶端
Future f = sendBReq(prxB, input); // 發起 B 請求,得 future
f.then(bind(&handleB, prxC)) // B 回包後處理,發起 C 請求
.then(bind(&handleC, current)); // C 回包後最終回包客戶端
// sendBReq 內部:
Promise promise;
callback = new BCallback(current, promise); // 持有 promise
prxB->async_call(callback, input);
return promise.getFuture(); // 返回綁定同一 shared state 的 future
// handleB 簽名:Future handleB(PrxC prx, const Future& f)
// 內部:f.get() 取 B 結果 → sendCReq → 返回 C 的 future
// then 機制自動將 C 的 future 扁平化傳遞給下一層
Future(讀端)與 Promise(寫端)通過 shared_ptr 共享狀態,解耦生產者/消費者生命週期。FutureObject(異步、需鎖)vs PromptFutureObject(即時值、無鎖),場景化優化。SequentialCallback + ForwardValue 遞歸剝離嵌套 Future,保證鏈式調用類型穩定。Callback 擦除具體函數類型,resolved_type 編譯期萃取返回值類型,實現泛型鏈式調用。setValue/registerCallback/doPendingCallbacks,調用者無需額外同步。掌握這些原理,不僅能正確使用 future/promise,更能在需要時擴展自定義異步組件(如帶超時的 future、可取消的 promise 等)。