network drift
network drift

Reputation: 79

Joining threads before program termination

I have a vector of threads that I collect during the execution of my application. To avoid some nasty behavior, I'm trying to ensure every thread is completed when the main thread exits. I tried to call std::thread::join on each thread in the vector upon a termination event, but it seems to get stuck if the most recent thread hasn't finished its work and won't stop blocking even after it should. It's important to note that ThingMaker::CreateThing reads frames from a series of video files and writes them all to one video, so I know the thread should finish its work in less time than the length of the video clip being created.

std::vector<std::thread> threadList;
while (!done)
{
    switch (triggerEvent)
    {
        case 'h': // Spawn new thread to make a thing "in the background"
        {
            ThingMaker next_thing = new ThingMaker();
            threadList.push_back(std::thread(&ThingMaker::CreateThing, next_thing));
            next_thing = NULL;
            break;
        }
        case 't': // Terminate the application
        {
            std::vector<std::thread>::iterator threads;
            for (threads = threadList.begin(); threads != threadList.end(); ++threads)
                threads->join();
            done = true;
            break;
        }
        default: break;
    }
}

If I send a 't' before the most recent thread has finished making the video clip and thus finished altogether, threads->join() blocks forever. However, if i wait for all video clips to be created, the application terminates. To my understanding, it should simply wait for the thread to finish its work and then let the main thread carry on - is this a misunderstanding?

Upvotes: 1

Views: 2253

Answers (4)

Jonathan Wakely
Jonathan Wakely

Reputation: 171383

The code you have shown (which is still woefully incomplete and so we're just guessing) doesn't remove thread objects from the vector after you join them. That means you will try to rejoin an already joined thread on the next time through the loop. Trying to join a thread that is not joinable has undefined behaviour. It could block forever (because there is no such thread so it can never be joined).

You should either:

  • only try to join threads that are joinable:

    case 't': // Terminate the application
    {
        for (auto& t : threadList)
        if (t.joinable())
            t.join();
        done = true;
        break;
    }
    
  • or remove the threads from the list after they've been joined:

    case 't': // Terminate the application
    {
        for (auto& t : threadList)
            t.join();
        threadList.clear();
        done = true;
        break;
    }
    

Upvotes: 0

David Schwartz
David Schwartz

Reputation: 182829

There are two possibilities:

  1. The thread simply hasn't finished yet. You can add logging to your code or use a debugger to see whether this is the case.

  2. The thread that called join holds some lock that is preventing the other thread from finishing. Calling join waits for a thread to finish and cannot be safely called unless you're absolutely sure that thread that calls join holds no locks the other thread might need to acquire in order to finish. (Look closely at the call stack leading to the call to join to see if this is a posssibility.)

Upvotes: 3

Christophe
Christophe

Reputation: 73456

There are two issues in your code:

  • The join() on an active thread will wait that the thread is finished before continuing anything. So if you have no mechanism to to tell your threads to stop (e.g. a shared atomic variable), you'll wait forever.

  • Your threadlist vector is inside the while loop, so that it's a new list on every occurence. What happens to the thread that you have pushed into it ? It gets destroyed, and as join() wasn't called, it will terminate() your programme.

Upvotes: 3

Johan Lundberg
Johan Lundberg

Reputation: 27038

With std::thread you must ensure that each thread is made unjoinable (that typically means calling .join()), exactly once. Clearly, a call to .join() will wait for that thread to complete. Start a debug session and have a look what each of the threads are doing.

Upvotes: 1

Related Questions