4
votes

If I launch std::async with std::launch::async policy shouldn't it start every async task in a new thread? At the moment it looks that new async tasks move to a thread which just completed it's work. I'm using VC11 as my compiler. As you can see from the output when a new worker (e.g. a worker gets a thread with ID 34500 multiple times) is launched with std::async, it starts in a previously finished thread. Is my understanding of std::async wrong or is there an underlying work stealing queue or something of that sort?

Worker (ID=24072) starting.
Worker (ID=34500) starting.
Worker (ID=32292) starting.
Worker (ID=31392) starting.
Worker (ID=17976) starting.
Worker (ID=31580) starting.
Worker (ID=33512) starting.
Worker (ID=33804) starting.
Worker 32292 finished.
Worker (ID=32292) starting.
Worker 17976 finished.
Worker (ID=17976) starting.
Worker 31580 finished.
Worker (ID=31580) starting.
Worker 34500 finished.
Worker (ID=34500) starting.
Worker 34500 finished.
Worker (ID=34500) starting.
Worker 32292 finished.
Worker (ID=32292) starting.
Worker 17976 finished.
Worker (ID=17976) starting.
Worker 34500 finished.
Worker 17976 finished. 
Worker 31580 finished.
Worker 32292 finished.
Worker 33804 finished.
Worker 31392 finished.
Worker 33512 finished.
Worker 24072 finished.
2
I think the implementation is allowed to use a thread pool for new tasks, or atleast that's the intent IIRC. - Xeo

2 Answers

11
votes

If I launch std::async with std::launch::async policy shouldn't it start every async task in a new thread?

The specification requires that the asynchronous operation be executed "as if in a new thread of execution" (C++11 ยง30.6.8/11). The important words here are: as if.

An already-running worker thread can be reused if and only if the behavior is the same as if a new thread was created. This means, for example, that variables with the thread_local storage class must be reset between asynchronous operations executed on a single thread.

It is not necessary that the thread identifier be reset because a thread identifier only uniquely identifies a thread while it is running. If a thread terminates, another thread may be started with the first thread's identifier.

Is there an underlying work stealing queue or something of that sort?

This is implementation-specific. The Visual C++ 2012 implementation of the C++11 thread support library is built atop the Concurrency Runtime (ConcRT), which includes a work-stealing task scheduler.

6
votes

James is right, with one correction: Microsoft implementation of launch::async is incorrect. This was discussed at length by the C++ concurrency working group in Redmond. Visual C++ 2012 is still in beta, so they will probably change the implementation to be Standard compliant.