Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::async not executing when specifying launch::async

Maybe I'm missing the correct usage of the new std::async in C++11, however this statement (over at cppreference.com):

If the async flag is set (i.e. policy & std::launch::async != 0), then async executes the function f on a separate thread of execution as if spawned by std::thread(f, args...), except that if the function f returns a value or throws an exception, it is stored in the shared state accessible through the std::future that async returns to the caller.

Makes me think that my thread should start immediately with this statement:

std::async(std::launch::async, MyFunctionObject());

Without having to wait for calling the std::future::get(). This seems to not be the case (compiling with MSVC 13). If this is not triggered by this statement itself, how should this get triggered if I don't care about the return value of the std::future object?

Example:

#include <thread>
#include <iostream>
#include <array>
#include <future>

static std::mutex write_mutex;

class Cpp11Threads {
public:
    // Function operator for Function-Object
    void operator()() {
        const int num_threads = 50;

        // Static std array
        std::array<std::thread*, num_threads> worker_threads;

        // Range based
        for (std::thread*& thread : worker_threads) {

            // Lambda expression
            thread = new std::thread(
                [] {
                    static int i = 0;
                    write_mutex.lock();
                    std::cout << "Hello, I am happy Std thread #" << i++ << std::endl;
                    write_mutex.unlock();
                });
        }

        for (std::thread*& thread : worker_threads) {
            thread->join();
            delete thread;

            // nullptr instead of NULL
            thread = nullptr;
        }
    }
};

int main() {
    std::async(std::launch::async, Cpp11Threads());
    return 0;
}
like image 339
Danny A Avatar asked Dec 19 '22 19:12

Danny A


2 Answers

I'm not proficient with C++11 but AFAIK each program has a main thread and it is the thread in which your main() function executes. When the execution of that thread finishes, the program finishes along with all its threads. If you want your main thread to wait for other threads, use must use something like

pthread_join

in a linux environment (if you created your thread manually), or exactly

std::future::get()

in this specific case.

Exiting main will kill your threads and in your case probably prevent you from launching the thread at all

like image 27
Marco A. Avatar answered Jan 07 '23 08:01

Marco A.


The first thing you have to know is that MSVC std::async does not conform to the C++11 standard.

Under the C++11 standard, std::async's std::future return value blocks until the std::async completes.

MSVC's implementation does not. This makes their std::async seemingly more friendly to use, but in practice it is quite tricky.

However, as std::async's behavior is described in terms of std::thread, we can look at what happens when you kick off a std::thread and fail to clean it up. The resulting std::thread is effectively detached. Once you exit main, the C++ standard does not specify what happens to such std::threads, leaving it up to your particular implementation.

Based off some quick research, when a MSVC windows program goes off the end of main, the threads are terminated.

In short, your program needs to resynchronize with the threads you launched in some way or another, so that they can complete their tasks, and prevent the main program from exiting main. An easy way to do that is to store the returned std::future from your async task, and wait on it before main exits.

If you had a conforming C++11 compiler, your attempted async would fail to be asynchronous, as it would block immediately upon the destruction of the anonymous std::future it returned.

Finally, note that launched threads and the like may not be scheduled to run immediately after creation. How and when they run is not predictable.

The C++11 concurrency primitives are merely primitives. Many of them have quirky behavior, like the fact that a std::thread calls terminate if it is destroyed without being detached or joined, and async's tendency to block if you don't store the future. They can be used for simple tasks, or for writing higher level libraries, but they are not user friendly.

like image 105
Yakk - Adam Nevraumont Avatar answered Jan 07 '23 09:01

Yakk - Adam Nevraumont