std::async
在標頭檔案 <future> 中定義 |
||
template< class F, class... Args > std::future</* 見下文 */> async( F&& f, Args&&... args ); |
(1) | (C++11 起) |
template< class F, class... Args > std::future</* 見下文 */> async( std::launch policy, |
(2) | (C++11 起) |
函式模板 std::async
非同步執行函式 f(可能在一個單獨的執行緒中,該執行緒可能是執行緒池的一部分),並返回一個 std::future,該 std::future 最終將儲存該函式呼叫的結果。
std::async
的返回型別是 std::future<V>,其中 V
是
typename std::result_of<typename std::decay<F>::type( |
(C++17 前) |
std::invoke_result_t<std::decay_t<F>, std::decay_t<Args>...>. |
(C++17 起) |
如果滿足以下任何條件,程式將不正確:
|
(C++20 前) |
如果以下任一條件為 false,則程式格式錯誤
|
(C++20 起) |
對 std::async
的呼叫與對 f 的呼叫 同步,並且 f 的完成 先於 共享狀態的就緒。
目錄 |
[編輯] 引數
f | - | 要呼叫的 可呼叫(Callable)物件 |
args | - | 傳遞給 f 的引數 |
policy | - | 位掩碼值,其中各個位控制允許的執行方法 |
[編輯] 返回值
std::future,引用此 std::async
呼叫建立的共享狀態。
[編輯] 啟動策略
[編輯] 非同步呼叫
如果設定了 async 標誌,即 (policy & std::launch::async) != 0,則 std::async
呼叫
INVOKE(decay-copy(std::forward<F>(f)), |
(直至 C++23) |
std::invoke(auto(std::forward<F>(f)), |
(C++23 起) |
如同在一個由 std::thread 物件表示的新執行執行緒中。
對 decay-copy 的呼叫在當前執行緒中求值。 |
(直至 C++23) |
由 auto 產生的值在當前執行緒中 實體化。 |
(C++23 起) |
如果函式 f 返回一個值或丟擲異常,它將儲存在透過 std::async
返回給呼叫者的 std::future 可訪問的共享狀態中。
[編輯] 延遲呼叫
如果設定了 deferred 標誌(即 (policy & std::launch::deferred) != 0),則 std::async
將
decay-copy(std::forward<F>(f)) 和 decay-copy(std::forward<Args>(args))... 儲存在共享狀態中。 |
(直至 C++23) |
auto(std::forward<F>(f)) 和 auto(std::forward<Args>(args))... 儲存在共享狀態中。 |
(C++23 起) |
執行延遲求值(Lazy evaluation)
- 對
std::async
返回給呼叫者的 std::future 的非定時等待函式的第一次呼叫將在呼叫等待函式的執行緒(不必是最初呼叫std::async
的執行緒)中評估 INVOKE(std::move(g), std::move(xyz)),其中
|
(直至 C++23) |
|
(C++23 起) |
- 結果或異常被放置在與返回的 std::future 關聯的共享狀態中,然後才使其就緒。所有後續對相同 std::future 的訪問都將立即返回結果。
[編輯] 其他策略
如果在 policy 中既沒有設定 std::launch::async 也沒有設定 std::launch::deferred,也沒有設定任何實現定義的策略標誌,則行為未定義。
[編輯] 策略選擇
如果設定了多個標誌,則選擇哪種策略由實現定義。對於預設情況(std::launch::async 和 std::launch::deferred 標誌都在 policy 中設定),標準建議(但不要求)利用可用的併發性,並延遲任何額外的任務。
如果選擇了 std::launch::async 策略,
- 對共享由
std::async
呼叫建立的共享狀態的非同步返回物件上的等待函式的呼叫將阻塞,直到關聯執行緒完成(如同已加入),或者超時;並且 - 關聯執行緒的完成與第一次等待共享狀態的函式的成功返回,或與釋放共享狀態的最後一個函式的返回同步,以先發生者為準。
[編輯] 異常
丟擲
- std::bad_alloc,如果無法分配內部資料結構的記憶體,或者
- std::system_error,錯誤條件為 std::errc::resource_unavailable_try_again,如果 policy == std::launch::async 且實現無法啟動新執行緒。
- 如果 policy 是 std::launch::async | std::launch::deferred 或設定了額外的位,則在此情況下它將回退到延遲呼叫或實現定義的策略。
[編輯] 注意
實現可以透過在預設啟動策略中啟用額外的(實現定義的)位來擴充套件 std::async
的第一個過載的行為。
實現定義的啟動策略示例包括同步策略(在 std::async
呼叫中立即執行)和任務策略(類似於 std::async
,但執行緒區域性變數不清空)
如果從 std::async
獲取的 std::future 未被移動或繫結到引用,則 std::future 的解構函式將在完整表示式的末尾阻塞,直到非同步操作完成,從而使以下程式碼本質上同步
std::async(std::launch::async, []{ f(); }); // temporary's dtor waits for f() std::async(std::launch::async, []{ g(); }); // does not start until f() completes
請注意,透過呼叫 std::async
以外的方式獲得的 std::future 的解構函式從不阻塞。
[編輯] 示例
#include <algorithm> #include <future> #include <iostream> #include <mutex> #include <numeric> #include <string> #include <vector> std::mutex m; struct X { void foo(int i, const std::string& str) { std::lock_guard<std::mutex> lk(m); std::cout << str << ' ' << i << '\n'; } void bar(const std::string& str) { std::lock_guard<std::mutex> lk(m); std::cout << str << '\n'; } int operator()(int i) { std::lock_guard<std::mutex> lk(m); std::cout << i << '\n'; return i + 10; } }; template<typename RandomIt> int parallel_sum(RandomIt beg, RandomIt end) { auto len = end - beg; if (len < 1000) return std::accumulate(beg, end, 0); RandomIt mid = beg + len / 2; auto handle = std::async(std::launch::async, parallel_sum<RandomIt>, mid, end); int sum = parallel_sum(beg, mid); return sum + handle.get(); } int main() { std::vector<int> v(10000, 1); std::cout << "The sum is " << parallel_sum(v.begin(), v.end()) << '\n'; X x; // Calls (&x)->foo(42, "Hello") with default policy: // may print "Hello 42" concurrently or defer execution auto a1 = std::async(&X::foo, &x, 42, "Hello"); // Calls x.bar("world!") with deferred policy // prints "world!" when a2.get() or a2.wait() is called auto a2 = std::async(std::launch::deferred, &X::bar, x, "world!"); // Calls X()(43); with async policy // prints "43" concurrently auto a3 = std::async(std::launch::async, X(), 43); a2.wait(); // prints "world!" std::cout << a3.get() << '\n'; // prints "53" } // if a1 is not done at this point, destructor of a1 prints "Hello 42" here
可能的輸出
The sum is 10000 43 world! 53 Hello 42
[編輯] 缺陷報告
下列更改行為的缺陷報告追溯地應用於以前出版的 C++ 標準。
缺陷報告 | 應用於 | 釋出時的行為 | 正確的行為 |
---|---|---|---|
LWG 2021 | C++11 | 返回型別不正確且在延遲情況下引數的 值類別不明確 |
已更正返回型別並 澄清使用右值 |
LWG 2078 | C++11 | 不清楚如果 policy 指定了除了 std::launch::async 之外的其他啟動策略, 是否會丟擲 std::system_error 只有當 policy 為 std::launch::async 時才可能丟擲 |
僅當 policy == std::launch::async 時才丟擲 |
LWG 2100 | C++11 | 定時等待函式無法超時 如果使用 std::launch::async 策略 |
允許 |
LWG 2120 | C++11 | 如果未設定標準 或實現定義的策略,則行為不明確 |
在這種情況下,行為是 未定義的 |
LWG 2186 | C++11 | 不清楚延遲求值返回的值和丟擲的 異常如何處理 |
它們儲存在 共享狀態中 |
LWG 2752 | C++11 | 如果無法分配內部資料結構的記憶體,std::async 可能不會丟擲 std::bad_alloc如果無法分配內部資料結構的記憶體 |
丟擲 |
LWG 3476 | C++20 | (衰變後的) F 型別和引數型別曾被直接要求為可移動構造的 |
已移除這些要求[1] |
- ↑ 可移動構造性已經透過 std::is_constructible_v 間接要求。
[編輯] 另見
(C++11) |
等待一個非同步設定的值 (類模板) |
C++ 文件 for 執行支援庫
|