Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ program unexpectedly blocks / throws

I'm learning about mutexes in C++ and have a problem with the following code (taken from N. Josuttis' "The C++ Standard Library").

I don't understand why it blocks / throws unless I add this_thread::sleep_for in the main thread (then it doesn't block and all three calls are carried out).

The compiler is cl.exe used from the command line.

#include <future>
#include <mutex>
#include <iostream>
#include <string>
#include <thread>
#include <chrono>

std::mutex printMutex;

void print(const std::string& s)
{
    std::lock_guard<std::mutex> lg(printMutex);

    for (char c : s)
    {
        std::cout.put(c);
    }
    std::cout << std::endl;
}

int main()
{
    auto f1 = std::async(std::launch::async, print, "Hello from thread 1");
    auto f2 = std::async(std::launch::async, print, "Hello from thread 2");

    // std::this_thread::sleep_for(std::chrono::seconds(1));

    print(std::string("Hello from main"));       
}
like image 371
Wojtek Avatar asked Oct 09 '14 12:10

Wojtek


3 Answers

I think what you are seeing is an issue with the conformance of the MSVC implementation of async (in combination with future). I believe it is not conformant. I am able to reproduce it with VS2013, but unable to reproduce the issue with gcc.

The crash is because the main thread exits (and starts to clean up) before the other two threads complete.

Hence a simple delay (the sleep_for) or .get() or .wait() on the two futures should fix it for you. So the modified main could look like;

int main()
{
    auto f1 = std::async(std::launch::async, print, "Hello from thread 1");
    auto f2 = std::async(std::launch::async, print, "Hello from thread 2");

    print(std::string("Hello from main"));       

    f1.get();
    f2.get();
}

Favour the explicit wait or get over the timed "sleep".

Notes on the conformance

There was a proposal from Herb Sutter to change the wait or block on the shared state of the future returned from async. This may be the reason for the behaviour in MSVC, it could be seen as having implemented the proposal. I'm not sure what the final result was of the proposal was or its integration (or part thereof) into C++14. At least w.r.t. the blocking of the future returned from async it looks like the MSVC behaviour did not make it into the specification.

It is interesting to note that the wording in §30.6.8/5 changed;

From C++11

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

To C++14

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

I'm not sure how the "time out" would be specified, I would imagine it is implementation defined.

like image 61
Niall Avatar answered Oct 06 '22 00:10

Niall


std::async returns a future. Its destructor blocks if get or wait has not been called:

it may block if all of the following are true: the shared state was created by a call to std::async, the shared state is not yet ready, and this was the last reference to the shared state.

See std::futures from std::async aren't special! for a detailed treatment of the subject.

like image 27
Maxim Egorushkin Avatar answered Oct 06 '22 00:10

Maxim Egorushkin


Add these 2 lines at the end of main:

f1.wait();
f2.wait();

This will make sure the threads finish before main exists.

like image 22
egur Avatar answered Oct 06 '22 01:10

egur