1

Here is my threaded program, which is very simple:

#include <iostream>
#include <thread>

using namespace std;


void myprint(int a)
{
    cout << a << endl;
}
int main()
{
    thread obj(myprint, 3);
    obj.detach();
    cout << "end!!!" << endl;
}

What I can't understand is that the program occasionally has sub-threads that print 3 multiple times, such as:

3
3
end!!!

I have browsed through many sources but have not found a good answer to this question.

I know that std::out is thread-unsafe and understand the garbled output due to race. The issue of duplicate output, however, doesn't seem to have much to do with race, as it doesn't occur in join() contexts.

The relevant configuration of the programming environment is as follows

Linux version 5.15.0-48-generic (buildd@lcy02-amd64-080)
(gcc (Ubuntu 11.2.0-19ubuntu1) 11.2.0,
 GNU ld (GNU Binutils for Ubuntu) 2.38) 

GCC version:

gcc version 11.2.0 
Abner
  • 29
  • 4
  • 3
    Detaching threads, while `main()` leaves the scope, rarely is a good idea. – πάντα ῥεῖ Oct 06 '22 at 14:24
  • 1
    Why don't you `join()` your thread? – Jesper Juhl Oct 06 '22 at 14:25
  • 2
    Btw; don't try to learn C++ by experimenting and guessing. Read [a good book](https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list) instead. – Jesper Juhl Oct 06 '22 at 14:27
  • 1
    https://stackoverflow.com/a/22804813/4165552 here nicely explained why you almost never want to use `detach` – pptaszni Oct 06 '22 at 14:27
  • 1
    You have written a program where `myprint` races with `std::cout`'s own destructor. – Drew Dormann Oct 06 '22 at 14:28
  • 1
    Once you start using threads you have to consider that they share a memory space. You need to synchronise access to shared resources. You need to wait for them to finish. And more; don't take threads lightly - they require *very careful care and attention*. – Jesper Juhl Oct 06 '22 at 14:33
  • `std::cout` is modified (its buffer or the output device are written to) in both threads, and those operations are unsequenced. Behaviour is therefore undefined. There are numerous operations on both threads (e.g. formatting data, copying data to `std::cout`s buffer, removing data from that buffer and copying to the buffer maintained by the terminal/window) on different threads. Those operations are not atomic, so may preempt/interrupt each other while some operations are partially incomplete - which can cause repeated effects like you see (or interleaved effects). – Peter Oct 06 '22 at 14:35
  • It would be interesting to know which compiler you're using, what version of it, what standard library you use, and of course your operating system. – Some programmer dude Oct 06 '22 at 14:37
  • 1
    You are probably in Undefined Behaviour land if `main` exits before the thread has completed. This is because after `main` exits the runtime support is being torn down and anything using it (eg IO) will not behave as expected ... looking for a reference the the above theory. – Richard Critten Oct 06 '22 at 14:46
  • 1
    @Peter Are you sure about it? See http://eel.is/c++draft/iostream.objects#overview-7. I don't agree with that undefined behavior. Yes, interleaved characters are possible. – Daniel Langr Oct 06 '22 at 15:05

2 Answers2

1

It's a buffering issue. Because the process itself only have one std::cout object and to that one connected buffer, it will be shared between all the threads.

When probably happens is that the thread prints the output, but doesn't manage to clear the buffer before the thread is preempted and the main thread runs to print its "end!!!" message. Since the buffer isn't clear it will also contain the 3 output from the thread, so it's written again. This is a race condition, and those are bad.

The simplest solution is to not detach the thread, and join it before you print the "end!!!" message and exit the process.

A more complex solution is to add some kind of synchronization between the threads in your processes, so all access to std::cout is protected and can run only mutually exclusive from all other access to it.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • 2
    doesn't `cout` have built in synchronization? IIRC it doesn't guarantee order but each operation should be synchronized against all others in some order. – NathanOliver Oct 06 '22 at 14:33
  • @NathanOliver There should be some synchronization, and `stdout` (which it often uses) also have some. But apparently for the OP's implementation the flushing doesn't seem to be atomic. – Some programmer dude Oct 06 '22 at 14:36
  • 1
    I'm guessing that cout's destructor is involved, because there is no reason to synchronize cout's internals in a destructor. – Drew Dormann Oct 06 '22 at 14:37
  • @DrewDormann I was thinking in those directions first, that it happened due to the process termination after `main` returns. But both threads uses `endl` which should flush before the `std::cout` object is destructed. – Some programmer dude Oct 06 '22 at 14:40
  • 1
    Could be running into a resurrection of the object at exit time via the use-after-destroy and the Nifty Counter idiom (aka Schwarz Counter idiom). – Eljay Oct 06 '22 at 15:08
  • some cases the output is that 'end!!! 3 3' – Abner Oct 06 '22 at 15:31
  • @Abner That could happen when the main thread runs first (you have no control over that), followed by the thread function, followed by process termination before the buffer is emptied. – Some programmer dude Oct 06 '22 at 15:39
1

Aha,this morning I had a stroke of genius and solve the problem successfully. You just need to test the cpu time that join() and detach() executed, you will get the answers.

I think @Someprogrammerdude is right, it's just that the join takes longer to execute, so the problem of duplicate output only occurs in detach()

Abner
  • 29
  • 4