C++11 threading gotchya

Simple Threading

I'm using the C++11 threading support for the first time, and I must say, I'm impressed. It contains most of the functionality from the Qt threading module (which is my formative experience of a "modern" C++ threading library), but manages to cut it down to just a few core concepts. One of these nice features is that if you have a function like this:

void my_fun()
{
        std::cout << "Hello from thread "
                << std::this_thread::get_id()
                << std::endl;
}

Running that function in a thread is as easy as:

std::thread my_thread(my_fun);

Which is great if you don't care about the return value. However, what about retrieving the result?

Now with std::async

Let's modify our earlier function, so it returns a value:

std::string my_fun()
{
        return std::string("Hello World");
}

We can run this in a thread and capture the result using std::async and std::future, like so:

std::future<std::string> future = std::async(std::launch::async, my_fun);
std::cout << future.get();

In this example, the first line runs my_fun in a new thread, and gives us a std::future. The second line waits for the thread to end, and returns the value returned by my_fun to us (it will also propogate exceptions, but that's a blog post for another day).

Obviously you'd probably want to do something between running the function and getting the result.

This is a really nice feature for certain tasks. It makes it trivial to fire off tasks and then get their results later.

The Problem

Using what you now know about std::async and std::future, consider the following full program listing:

#include <thread>
#include <future>

int my_fun()
{
    std::this_thread::sleep_for(std::chrono::seconds(5));
    return 42;
}

int main(int argc, char** argv)
{
    std::async(std::launch::async, my_fun );
    std::async(std::launch::async, my_fun );
        return 0;
}

How long do you expect this program to run? A casual glance might lead you to believe that the code should run the two tasks in parallel, whereas the reality is very different.

I believe what is happening is that the std::future destructor waits for the task to finish. Since we're ignoring the std::future being returned from std::async, the destructor is being called immediately. This in turn means that each call to std::async takes five seconds to run, which is not what we expected.


comments powered by Disqus