3

I have this small program that I found in an exam subject of an OS course.

void * func (void * p) {
    int n = p;
    printf("%d \n",n);
    return NULL;
}

int main() {
    int i;
    pthread_t t[3];
    for(i=0; i<3; i+=1)
        pthread_create(&t[i] ,NULL, func, (void*)i);
    return 0;
}

When I run it, I get the following results (with a new line after each digit):

1st run : 0 0
2nd run : 1 0 2 2
3rd run : 0 1 1

Why does it print 4 digits when I only create 3 threads. And how can it print duplicates?

The code is compiled with gcc in Ubuntu.

screenshot of the terminal

lmcanavals
  • 2,339
  • 1
  • 24
  • 35
Sbiera Bogdan
  • 346
  • 1
  • 11
  • I've been experimenting with this program, and I cannot reproduce the behaviour. – NPE Jan 27 '13 at 23:37
  • i didn't get them in that exact order, most of the runs i didn't get any output(which is understandable, because the main process ends before the created threads are executed), sometimes i got 0, 0 1 , 0 2 ,2 0 (which are also expected). But I can't see how i could get the above outputs. I will upload a screen shot of the terminal to prove it. – Sbiera Bogdan Jan 27 '13 at 23:39
  • Maybe the parent thread terminates before the children? – user000001 Jan 27 '13 at 23:40
  • Please also include the exact command line you give to your compiler when building the code. – NPE Jan 27 '13 at 23:40
  • 1
    Since you haven't *joined* with main thread, it's expected to get less than 3 values printed for some runs when main threads exits before others. But I don't know how this will ever print 4 values in any run. – P.P Jan 27 '13 at 23:43
  • gcc -Wall -o 1 1.c -lpthread – Sbiera Bogdan Jan 27 '13 at 23:43
  • This question has been asked several times, but never with an answer that I think is satisfactory. For example, http://stackoverflow.com/q/10322175/12711 and http://stackoverflow.com/q/13550662/12711 - Maybe someone will give a good answer this time... – Michael Burr Jan 28 '13 at 02:43
  • `$ cat out.txt | sort -n | tail` yields these: `0210 210 210 210 210 210 210 210 1002 01012` – danuker Jan 29 '13 at 10:59

2 Answers2

7

You do not join your threads before exiting main(). Add the following into main():

for(i=0; i<3; i+=1)
    pthread_join(t[i], NULL);

Not joining the threads leads to undefined behavior when the threads continue to execute while the program is exiting. Undefined behavior is free to do anything, including printing duplicates.

Think of it this way, the void* that is passed to the thread is stored somewhere, and once you prematurely exit main you could be destructing the data to pass to the thread, at which point it can take on any value (including duplicate ones). But this isn't even worth trying to explain since it is undefined behavior.

JaredC
  • 5,150
  • 1
  • 20
  • 45
  • 1
    Do you happen to have a reference for *Not joining the threads leads to undefined behavior when your `pthread_t`s are destructed while the thread they're associated with runs in the background.*? – NPE Jan 27 '13 at 23:50
  • Well, undefined behavior often *can* be explained, knowing what's going on under the hood (i.e. going besides the specs and looking at the implementation). The problem here is that it's particularly complicated to dissect the problem, since the behavior is substantially non-deterministic. – Matteo Italia Jan 27 '13 at 23:51
  • @Matteo Removed the *unexplainable* part to avoid confusion. – JaredC Jan 27 '13 at 23:55
  • @NPE I removed that bit since technically `pthread_t` is just an identifier, and I won't go into the details of launching a thread. – JaredC Jan 28 '13 at 00:00
2

return from the main function is equivalent to a call to exit and terminates the whole process. So its more or less random which output makes it through to your screen. You should do one of the following

  • join all threads that you create
  • call pthread_exit at the end of main instead of calling exit or using return
Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
  • in the exam, this code is given , and it asks which of the following will print : (_)(_)(_)0 0. ALWAYS/SOMETIMES/NEVER (_)(_)(_)Nothing. ALWAYS/SOMETIMES/NEVER (_)(_)(_)2 0 0 ALWAYS/SOMETIMES/NEVER (_)(_)(_)0 1 2 ALWAYS/SOMETIMES/NEVER (_)(_)(_)2 0 . ALWAYS/SOMETIMES/NEVER and i would have answered that i could never output 0 0 (but after i ran the code i was proven wrong) – Sbiera Bogdan Jan 27 '13 at 23:50
  • @user2016514, with the answers you should be able to find that out. – Jens Gustedt Jan 27 '13 at 23:53
  • Since you got into UB, the correct reply is "sometimes" for every one. Actually, running the executable in a loop it managed to produce `0�:զ����������%զ�@������`զ�� �����������:զ���������'զ�����`զ�`զ�`զ�`զ��!զ��"զ�`#զ��桦�`�Ԧ�`����wg70 ` (and other random garbage as well). – Matteo Italia Jan 27 '13 at 23:53