If launch::async is set in policy, calls
INVOKE(decay-copy(std::forward<F>(f)),decay-​copy(std::forward<Args>(args))...)
([func.require], [thread.thread.constr])
as if in a new thread of execution represented by a thread object
with the calls to decay-copy being evaluated in the thread that called async.

Invocation of the deferred function evaluates
INVOKE(std::move(g), std::move(xyz)) where g is the stored value of
decay-copy(std::forward<F>(f)) and xyz is the stored copy of
decay-copy(std::forward<Args>(args))....

The first call to a
non-timed waiting function ([futures.state])
on an asynchronous return object referring to
this shared state shall invoke the
deferred function in the thread that called the waiting function.

Once evaluation of INVOKE(std::move(g), std::move(xyz)) begins, the function is no longer
considered deferred.

If this policy is specified together with other policies, such as when using a
policy value of launch::async | launch::deferred, implementations should defer
invocation or the selection of the policy when no more concurrency can be effectively
exploited.

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]);

the associated thread completion
synchronizes with
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.

Line #1 might not result in concurrency because
the async call uses the default policy, which may use
launch::deferred, in which case the lambda might not be invoked until the
get() call; in that case, work1 and work2 are called on the
same thread and there is no concurrency.