1

I have a sample code:

#include <iostream>       // std::cout
#include <thread>         // std::thread
void pause_thread(int n)
{
    if(n != 4)
    {
        std::this_thread::sleep_for(std::chrono::seconds(100));
        std::cout << "pause of " << 100 << " seconds ended\n";
    }
    std::cout << "Thread number " << n << " ended\n";
}

int main()
{
    std::thread threads[6];                         // default-constructed threads
    std::setvbuf(stdout, NULL, _IONBF, 0);
    std::cout << "Spawning 5 threads...\n";
    for(int i = 0; i < 5; ++i)
    {
        //If the object is currently not joinable, it acquires the thread of execution represented by rhs (if any).
        //If it is joinable, terminate() is called. If it is joinable, terminate() is called.
        //rhs no longer represents any thread of execution
        threads[i] = std::move(std::thread(pause_thread, i));    // move-assign threads
    }
    std::thread& i = threads[4];
    threads[5] = std::move(threads[4]);
    std::cout << "Done spawning threads. Now waiting for them to join:\n";
    for(int i = 0; i < 6; ++i)
    {
        if(threads[i].joinable())
        {
            std::cout << "Thread " << i << " " << threads[i].get_id() << " ID joinable" << std::endl << std::flush;
            threads[i].join();
        }
        else
        {
            std::cout << "Thread " << i << " not joinable" << std::endl << std::flush;
        }
    }

    std::cout << "All threads joined!\n";


    return 0;
}

Below is the output I received:

Spawning 5 threads...  
Done spawning threads. Now waiting for them to join:     
Thread 0 22476 ID joinable   
Thread number 4 ended  
.... no output for 100 seconds ..
pause of 100 seconds ended
Thread number 0 ended  
pause of 100 seconds ended 
Thread 1 28676 ID joinable    
pause of 100 seconds ended 
Thread number 2 ended       
Thread number 3 ended  
pause of 100 seconds ended   
Thread number 1 ended        
Thread 2 2336 ID joinable    
Thread 3 42236 ID joinable  
Thread 4 not joinable        
Thread 5 35940 ID joinable  
All threads joined!  

How the "Thread n xxxx ID joinable" statements are getting printed after "Thread number n ended"? I have even tried using set std::output as non buffered but the output was same?

Programmer
  • 8,303
  • 23
  • 78
  • 162
  • 1
    what other output did you expect? I dont see the problem with what you got. First the threads print something and then you print something when you check if they are joinable and when you join them – 463035818_is_not_an_ai Sep 17 '20 at 15:30
  • 1
    You're not flushing the `cout` in `pause_thread`, so it could get printed like you see. – cigien Sep 17 '20 at 15:30
  • 2
    `std::cout` on its own is not thread safe afaik – 463035818_is_not_an_ai Sep 17 '20 at 15:31
  • I was expecting that all "Thread n xxxx ID joinable" should get printed first and if "Thread number n ended" then the thread n becomes non-joinable so "Thread n not joinable" should get printed as the thread n has already terminated – Programmer Sep 17 '20 at 15:34
  • 2
    @idclev463035818 _`std::cout` on its own is not thread safe afaik_ AFAIK, `std::iostream` _is_ thread-safe. [SO: iostream thread safety, must cout and cerr be locked separately?](https://stackoverflow.com/a/14637716/7478597) – Scheff's Cat Sep 17 '20 at 15:35
  • @Scheff oh, I am still carrying some pre-C++11 beliefs with me. Thx for busting that one – 463035818_is_not_an_ai Sep 17 '20 at 15:39
  • Unrelated: You could rewrite `for(int i = 0; i < 6; ++i) { if(threads[i].joinable()) ...` as `for(auto& th : threads) { if(th.joinable()) ...` to keep magic numbers like `6` out of it. – Ted Lyngmo Sep 17 '20 at 15:42
  • `main` (thread) stop at first loop `threads[0].join();` so you effectively doesn't wait at any thread (since they all have pass 100 second when `threads[0]` successfully joined.) – apple apple Sep 17 '20 at 15:51
  • and as others said, ended thread is `joinable` https://en.cppreference.com/w/cpp/thread/thread/joinable – apple apple Sep 17 '20 at 15:52
  • Then why "Thread 4 not joinable" gets logged? – Programmer Sep 17 '20 at 15:54
  • `<< std::endl << std::flush` is superfluous. – Daniel Langr Sep 17 '20 at 16:05

2 Answers2

2

"Joinable" does not imply that the thread is still executing.

You first join thread #0. This will take ~100 seconds.
During that time, thread #4 finishes since it doesn't sleep, and the other threads are sleeping.
If the threads happen to be scheduled differently, any of the "sleep threads" could be printing that they've ended here.

Once the wait for thread #0 is over, you start joining the other threads.
Some of these have finished executing before you join them and some haven't.
In this particular instance, none of them finished before the wait for thread #0 was over, but there is no guarantee of that happening.

And note that a line like

std::cout << "Thread number " << n << " ended\n";

is not atomic and characters from different threads can be interleaved.

molbdnilo
  • 64,751
  • 3
  • 43
  • 82
0

Because joinable does not mean what you think : https://en.cppreference.com/w/cpp/thread/thread/joinable

So any thread that is started is "joinable" it does not need to have finished running.

Yann TM
  • 1,942
  • 13
  • 22