191 lines
7.6 KiB
Markdown
191 lines
7.6 KiB
Markdown
[futures.async]
|
||
|
||
# 32 Concurrency support library [[thread]](./#thread)
|
||
|
||
## 32.10 Futures [[futures]](futures#async)
|
||
|
||
### 32.10.9 Function template async [futures.async]
|
||
|
||
[1](#1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L12373)
|
||
|
||
The function template async provides a mechanism to launch a function potentially
|
||
in a new thread and provides the result of the function in a future object with which
|
||
it shares a shared state[.](#1.sentence-1)
|
||
|
||
[ð](#lib:async)
|
||
|
||
`template<class F, class... Args>
|
||
future<invoke_result_t<decay_t<F>, decay_t<Args>...>>
|
||
async(F&& f, Args&&... args);
|
||
template<class F, class... Args>
|
||
future<invoke_result_t<decay_t<F>, decay_t<Args>...>>
|
||
async(launch policy, F&& f, Args&&... args);
|
||
`
|
||
|
||
[2](#2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L12389)
|
||
|
||
*Mandates*: The following are all true:
|
||
|
||
- [(2.1)](#2.1)
|
||
|
||
is_constructible_v<decay_t<F>, F>,
|
||
|
||
- [(2.2)](#2.2)
|
||
|
||
(is_constructible_v<decay_t<Args>, Args> && ...), and
|
||
|
||
- [(2.3)](#2.3)
|
||
|
||
is_invocable_v<decay_t<F>, decay_t<Args>...>[.](#2.sentence-1)
|
||
|
||
[3](#3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L12398)
|
||
|
||
*Effects*: The first function
|
||
behaves the same as a call to the second function with a policy argument oflaunch::async | launch::deferred and the same arguments for F and Args[.](#3.sentence-1)
|
||
|
||
The second function creates a shared state that is associated with
|
||
the returned future object[.](#3.sentence-2)
|
||
|
||
The further behavior
|
||
of the second function depends on the policy argument as follows (if
|
||
more than one of these conditions applies, the implementation may choose any of
|
||
the corresponding policies):
|
||
|
||
- [(3.1)](#3.1)
|
||
|
||
If launch::async is set in policy, callsinvoke(auto(std::forward<F>(f)), auto(std::forward<Args>(args))...) ([[func.invoke]](func.invoke "22.10.5 invoke functions"), [[thread.thread.constr]](thread.thread.constr "32.4.3.3 Constructors"))
|
||
as if in a new thread of execution represented by a thread object
|
||
with the values produced by auto being materialized ([[conv.rval]](conv.rval "7.3.5 Temporary materialization conversion")) in the thread that called async[.](#3.1.sentence-1)
|
||
Any return value
|
||
is stored as the result in the
|
||
shared state[.](#3.1.sentence-2)
|
||
Any exception propagated from
|
||
the execution ofinvoke(auto(std::forward<F>(f)), auto(std::forward<Args>(args))...) is stored as the exceptional result in the shared state[.](#3.1.sentence-3)
|
||
The thread object is
|
||
stored in the shared state
|
||
and affects the behavior of any asynchronous return objects that
|
||
reference that state[.](#3.1.sentence-4)
|
||
|
||
- [(3.2)](#3.2)
|
||
|
||
If launch::deferred is set in policy,
|
||
stores auto(std::forward<F>(f)) andauto(std::forward<Args>(args))... in the shared state[.](#3.2.sentence-1)
|
||
These copies of f and args constitute
|
||
a [*deferred function*](#def:function,deferred "32.10.9 Function template async [futures.async]")[.](#3.2.sentence-2)
|
||
Invocation of the deferred function evaluatesinvoke(std::move(g), std::move(xyz)) where g is the stored value ofauto(std::forward<F>(f)) and xyz is the stored copy ofauto(std::forward<Args>(args))...[.](#3.2.sentence-3)
|
||
Any return value is stored
|
||
as the result in the shared state[.](#3.2.sentence-4)
|
||
Any exception propagated
|
||
from the execution
|
||
of the deferred function
|
||
is stored as the exceptional result
|
||
in the shared state[.](#3.2.sentence-5)
|
||
The shared state is not
|
||
made ready until the function has completed[.](#3.2.sentence-6)
|
||
The first call to a
|
||
non-timed waiting function ([[futures.state]](futures.state "32.10.5 Shared state"))
|
||
on an asynchronous return object referring to
|
||
this shared state invokes the
|
||
deferred function in the thread that called the waiting function[.](#3.2.sentence-7)
|
||
Once evaluation of invoke(std::move(g), std::move(xyz)) begins, the function is no longer
|
||
considered deferred[.](#3.2.sentence-8)
|
||
*Recommended practice*: If this policy is specified together with other policies, such as when using apolicy value of launch::async | launch::deferred, implementations should defer
|
||
invocation or the selection of the policy when no more concurrency can be effectively
|
||
exploited[.](#3.2.sentence-9)
|
||
|
||
- [(3.3)](#3.3)
|
||
|
||
If no value is set in the launch policy, or a value is set that is neither specified
|
||
in this document nor by the implementation, the behavior is undefined[.](#3.3.sentence-1)
|
||
|
||
[4](#4)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L12464)
|
||
|
||
*Synchronization*: The invocation of async synchronizes with the invocation of f[.](#4.sentence-1)
|
||
|
||
The completion of the function f is sequenced before
|
||
the shared state is made ready[.](#4.sentence-2)
|
||
|
||
[*Note [1](#note-1)*:
|
||
|
||
These apply regardless of the provided policy argument, and
|
||
even if the corresponding future object is moved to another thread[.](#4.sentence-3)
|
||
|
||
However, it is possible for f not to be called at all,
|
||
in which case its completion never happens[.](#4.sentence-4)
|
||
|
||
â *end note*]
|
||
|
||
If the implementation chooses the launch::async policy,
|
||
|
||
- [(4.1)](#4.1)
|
||
|
||
a call to a waiting function on an asynchronous return
|
||
object that shares the shared state created by this async call shall
|
||
block until the associated thread has completed, as if joined, or else time
|
||
out ([[thread.thread.member]](thread.thread.member "32.4.3.6 Members"));
|
||
|
||
- [(4.2)](#4.2)
|
||
|
||
the associated thread completion[synchronizes with](intro.multithread#def:synchronize_with "6.10.2 Multi-threaded executions and data races [intro.multithread]") the return from
|
||
the first function
|
||
that successfully detects the ready status of the shared state or
|
||
with the return from the last
|
||
function that releases the shared state, whichever
|
||
happens first[.](#4.sentence-5)
|
||
|
||
[5](#5)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L12494)
|
||
|
||
*Returns*: An object of typefuture<invoke_result_t<decay_t<F>, decay_t<Args>...>> that refers
|
||
to the shared state created by this call to async[.](#5.sentence-1)
|
||
|
||
[*Note [2](#note-2)*:
|
||
|
||
If a future obtained from async is moved outside the local scope,
|
||
the future's destructor can block for the shared state to become ready[.](#5.sentence-2)
|
||
|
||
â *end note*]
|
||
|
||
[6](#6)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L12504)
|
||
|
||
*Throws*: system_error if policy == launch::async and the
|
||
implementation is unable to start a new thread, orstd::bad_alloc if memory for the internal data structures
|
||
cannot be allocated[.](#6.sentence-1)
|
||
|
||
[7](#7)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L12511)
|
||
|
||
*Error conditions*:
|
||
|
||
- [(7.1)](#7.1)
|
||
|
||
resource_unavailable_try_again â ifpolicy == launch::async and the system is unable to start a new thread[.](#7.sentence-1)
|
||
|
||
[8](#8)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L12519)
|
||
|
||
[*Example [1](#example-1)*: int work1(int value);int work2(int value);int work(int value) {auto handle = std::async([=]{ return work2(value); }); int tmp = work1(value); return tmp + handle.get(); // #1}
|
||
|
||
[*Note [3](#note-3)*:
|
||
|
||
Line #1 might not result in concurrency because
|
||
the async call uses the default policy, which might uselaunch::deferred, in which case the lambda might not be invoked until theget() call; in that case, work1 and work2 are called on the
|
||
same thread and there is no concurrency[.](#8.sentence-1)
|
||
|
||
â *end note*]
|
||
|
||
â *end example*]
|