10
votes

Does standard C++11 guarantee that std::async(std::launch::async, func) launches function in separate thread?

Working Draft, Standard for Programming Language C++ 2016-07-12: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/n4606.pdf

1. On the one hand, C++11-Standard says that if the thread can not be created, then there is an error. This ensures the creation of a new thread (in the absence of errors).

§ 30.6.8

6

Throws: system_error if policy == launch::async and the implementation is unable to start a new thread.

7 Error conditions:

(7.1) — resource_unavailable_try_again — if policy == launch::async and the system is unable to start a new thread.

And documentation says: http://en.cppreference.com/w/cpp/thread/launch

std::launch::async a new thread is launched to execute the task asynchronously

2. On the other hand, it is written that the thread can be potentially created. Those, is not necessary that thread should be created.

§ 30.6.8

1 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.

And here written as if in a new thread, does it mean not necessary in new separate thread?

§ 30.6.8

(3.1)

— if policy & launch::async is non-zero — calls INVOKE (DECAY_COPY (std::forward(f)), DECAY_COPY (std::forward(args))...) (20.14.2, 30.3.1.2) 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. Any return value is stored as the result in the shared state. Any exception propagated from the execution of INVOKE (DECAY_COPY (std::forward(f)), DECAY_COPY (std::forward(args))...) is stored as the exceptional result in the shared state. The thread object is stored in the shared state and affects the behavior of any asynchronous return objects that reference that state.

When used std::async(std::launch::async, func) then does standard C++11 guarantee that func() will be executed in separate thread, or it can be executed in the same thread that called async?

1
I read "as if in a new thread" to mean "as if you had called std::thread(func)"lcs
I take "as if" to mean that the observable behaviour is as if you were in a new thread. For instance, thread-local variables would be initialized and destroyed.Kerrek SB
Doesn't look the it guarantees a thread will be spawned but it looks to guarantee that the behavior of the program will be as if you did. It's probably worded that way to allow some clever optimizations.NathanOliver
What if no thread is needed, eg if IO completion ports are used? In all languages a future or async feature allows the application to start an asynchronous operation and process its result when it returns. That doesn't mean that a thread will be used, or that the thread will be a new one. With real asynchronous operations (typically IO) you may not need a thread at all. If the implementation uses threadpools, you may get an idle thread from the pool instead of starting a new one.Panagiotis Kanavos
@Panagiotis Kanavos Yes, may be you are right, may be it can use already created thread from OS/C++-runtime thread-pool, if it uses IO Completion Ports (Proactor design pattern). But question is not "whether or not a new thread will be created", but question is "will be func() executed in the thread another than thread that called async()"? Those, does this guarantee that I can occupy two available CPU-cores by using std::launch::async to accelerate my programm, on condition if (std::thread::hardware_concurrency() >= 2)?Alex

1 Answers

6
votes

The two key statements here are:

as if in a new thread of execution represented by a thread object

The thread object is stored in the shared state and affects the behavior of any asynchronous return objects that reference that state.

"As if" means it must behave exactly as if it had created a std::thread object for this function. Which means that all side effects of the creation of a std::thread must also happen.

That being said, if you combine launch::async with launch::deferred, then the implementation decides whether to launch a new thread or defer it to an existing one. So it's only launch::async alone that requires a new thread.