Home > front end >  C function prints twice, only called once with a thread
C function prints twice, only called once with a thread

Time:10-05

So i ran into a situation in my operating systems course, where it seems as if a thread or function somehow runs twice even though it shouldn't. My teacher was as confounded as i was that this happened, and that it's reproducible. If i run it enough times, sure enough eventually it will produce the same result, where the message is printed twice. Anyone have any idea why and how this is happening?

See code below

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>

void *PrintMessage1()
{
    printf("message from thread1\n");
}

void *PrintMessage2()
{
    printf("this is thread2\n");
}

int main()
{
    pthread_t Thread1, Thread2;
    int ret;

    ret = pthread_create(&Thread1, NULL, PrintMessage1, NULL);
    if (ret) {printf("Thread1 creation error\n"); exit(1);}

    ret = pthread_create(&Thread2, NULL, PrintMessage2, NULL);
    if (ret) {printf("Thread2 creation error\n"); exit(2);}

    printf("this is main\n");
}

Below is the output i get occasionally. It happens with both thread 1 and thread 2.

this is main
message from thread1
message from thread1

I'm running it in Ubuntu 22.04. I use nano for editing and cc for compiling.

  • cc (Ubuntu 11.2.0-19ubuntu1) 11.2.0

CodePudding user response:

Misc notes/code review:

  • printf ought to be re-entrant since at least some 20 years back. If you have earlier versions still, from a time before POSIX settled, then all bets are off. I remember using pthreads back in school around year 2000 and back then the status of the standard libs in regards to thread safety was quite shaky.

  • Since you don't wait for (join) the threads to finish, this might just print this is main and quit. That would be conforming behavior.

  • Declaring a function returning void* without a return statement is wildly undefined behavior. Your compiler ought to tell you as much. Note that gcc is sloppy here and requires -Wall -Wextra. Whereas for example clang warns without getting prodded with a stick first. I'd call that a gcc bug.

  • Empty parenthesis in C means "accept any argument" and it's an obsolete form since some decades back. Correctly declared functions look like void func (void). However, pthreads require a callback in the form of void* func (void*).

    The compatibility between void* (*)() and void* (*)(void*) is a "language-lawyer" topic of it's own, but regardless you are using the wrong function pointer formats.

There's no such thing as "I almost wrote the code correctly" in C programming :) So change to functions looking like this:

void* PrintMessage1 (void* arg)
{
  printf("message from thread1\n");
  return 0;
}

CodePudding user response:

There are multiple issues in your code:

  • the thread functions should have the proper prototype: take a void * argument and return a pointer. Change them to this:

      void *PrintMessage1(void *arg) {
          printf("message from thread1\n");
          return NULL;
      }
    
      void *PrintMessage2(void *arg) {
          printf("this is thread2\n");
          return NULL;
      }
    
  • you should wait for the threads to complete before exiting the main function.

  • as commented by @xienne in Why is my thread function running twice? , there seems to be a race condition when flushing stdout that causes the buffer to be written twice to stdout. You could write to stderr instead or set stdout to be unbuffered to further investigate this problem, but the proper solution is to wait for the completion of the threads with pthread_join().

Here is a modified version:

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>

void *PrintMessage1(void *arg) {
    printf("message from thread1\n");
    return NULL;
}

void *PrintMessage2(void *arg) {
    printf("this is thread2\n");
    return NULL;
}

int main() {
    pthread_t Thread1, Thread2;
    int ret;

    ret = pthread_create(&Thread1, NULL, PrintMessage1, NULL);
    if (ret) {
        printf("Thread1 creation error\n");
        exit(1);
    }
    ret = pthread_create(&Thread2, NULL, PrintMessage2, NULL);
    if (ret) {
        printf("Thread2 creation error\n");
        exit(2);
    }
    printf("this is main\n");

    pthread_join(Thread1, NULL);
    pthread_join(Thread2, NULL);

    return 0;
}
  • Related