如何從線程獲取結果
首先可以傳遞結果引用給線程函數,這種方法需要額外的同步機制確定結果已經就緒;此外在C++11中可使用如下多種方法:std::async可以啟動一個新線程并執行,其結果保存在返回的std::future中;std::packaged_task<>將一個future綁定到一個函數或可調用對象,當std::packaged_task<>對象被調用后將綁定的函數或可調用對象的返回值作為關聯數據存儲;將std::promise
std::future
獲取
- get()
- 結果只能被取出一次,因此future可能處于valid或invalid狀態
- get() 對返回值進行move assign操作
- 結果只能被取出一次,因此future可能處于valid或invalid狀態
由于future的結果只能被取出一次,對同一future的多次get沒有意義(是未定義操作),但可以使用std::shared_future
等待
- wait()
- wait_for()
- wait_until()
析構
如果調用析構函數的那個future是某一shared state的最后持有者,而相關的task已啟動但尚未結束,析構函數會造成阻塞,直到任務結束
std::shared_future
- std::future調用share()方法得到
- 將std::future直接賦值得到
- 可多次調用get()
- 返回一個指向”存儲于shared state“的值的const reference,需要注意懸掛引用
生成std::future的三種方式
- std::async
- std::promise
- std::packaged_task
std::async
在不需要立刻得到一個結果時,可以使用 std::async 來啟動一個異步任務(在一個分享線程內)。std::async 返回一個 std::future 對象,它最終將持有函數的返回值。在 future 上調用 get() 方法(或 wait() 方法)可能會發生以下三種情況:
- func已經執行結束,立刻返回結果
- func已被啟動但尚未結束,阻塞住直到func結束(future就緒)
- func尚未啟動,強迫啟動如同一個同步調用并阻塞等待
注意調用 std::async 并不保證傳入的函數一定會被啟動和結束,但生產環境中的高并發服務其操作系統底層平臺都應該能夠支持多線程的,所以我們主要關注能夠實現并發的情況
調用形式
- future async (std::launch::async, F func, args...)
- 異步任務,創建一個并行線程
- future async (std::launch::deferred, F func, args...)
- 推遲任務,當對返回的future調用wait()或get()時被同步調用
- future async (F func, args...)
- 系統會根據當前形勢選擇兩者之一的發射策略
參考上述future析構的情況,如果async返回的future未被使用的話,對此次async的調用將會阻塞直到func完成(返回的臨時future調用析構函數會阻塞),即退化為同步調用
支持右值參數移動操作
std::packaged_task
std::packeaged_task<>周時持有目標函數及其可能的結果,典型的應用場景如:thread pool可控制何時運行以及多少個后臺task同時運行
執行
- operator()
- 調用目標函數
設置值
- reset()
- make_ready_at_thread_exit()
獲取future
- get_future()
- 只能調用一次
std::promise
std::async、std::packaged_task 都是在返回時將結果保存在std::future中,并且只返回一個future;對于那些不能以一個函數的方式通過調用它并獲取返回值的任務,或者其結果來自多個地方時,可以使用std::promise
std::promise
設置值
-
set_value()
- 使相關聯的future就緒
-
set_value_at_thread_exit()
- 為確保調用設置值的線程的local object及其他對象在”結果被處理前有所清除“,即令sahred state在線程結束時才就緒
獲取future
- get_future()
- 只能調用一次
處理異常
待補充
參考
《C++標準庫(第2版)》
《C++ Concurrency IN ACTION》
浙公網安備 33010602011771號