overworked student
overworked student

Reputation: 69

pthreads in C: How can I stop the threads from interfering with each other?

I'm new to using pthreads. I want to create a program where six different threads will each output a different number. The threads can run in any order, however, each thread should run only once.

So, a possible output would be:

Thread: 5
Thread: 2
Thread: 3
Thread: 6
Thread: 1
Thread: 4

Or it could be in any other order.

#include<stdio.h>
#include<pthread.h>
    
void *apples(void *void_apple_num){
      int *thread_num = (int *) void_apple_num;
      printf("Thread: %d\n", *thread_num);
      return NULL;
}



int main(){
  pthread_t threads[6];
  int apple_num;
  

  for(apple_num=0; apple_num<6; apple_num++){
       pthread_create(&threads[apple_num], NULL, apples, &apple_num);
  }

  for(apple_num=0; apple_num<6; apple_num++){
       pthread_join(threads[apple_num], NULL);
  }    

  return 0;
}

When I run the program, I have this problem of the threads interfering with each other. I am not sure if some of the threads are running twice? However, I think the problem is some of the threads are using the apple_num pointer from a different thread.

Here are two sample outputs I got:

Output 1:

Thread: 5
Thread: 0
Thread: 1
Thread: 1
Thread: 2
Thread: 2

Output 2:

Thread: 1
Thread: 4
Thread: 4
Thread: 5
Thread: 1
Thread: 1

I don't fully understand what is causing the problem. I've heard that threads can sometimes share variables? I don't know if I should use a mutex lock to somehow get the threads to run one-at-a-time.

How can I edit my code to solve this?

If somebody else has asked a similar question, please direct me to their question. I couldn't find anything when I researching.

Upvotes: 2

Views: 820

Answers (1)

Maxim Egorushkin
Maxim Egorushkin

Reputation: 136256

Each of your threads gets a pointer to the very same local variable apple_num which is being changed in the loop by the main thread. Since threads start asynchronously, the value of local variable apple_num in the main thread is indeterminate from the perspective of any other thread.

You need to pass a copy of that variable to each thread.

One fix is to cast int to void* and back:

void *apples(void* apple_num){
    int thread_num = (int)void_apple_num;
...
    pthread_create(&threads[apple_num], NULL, apples, (void*)apple_num);

As they mention in the comments, intptr_t and uintptr_t (from <stdint.h>) may be more appropriate for round-trip without loss, e.g. uintptr_t -> void* -> uintptr_t. But C standard doesn't require any integers to round-trip to void* and back, it only requires void* -> intptr_t and back.

In a more realistic scenario, you may like to pass more than just one integer to a thread, namely, a struct. And that's the rationale for the thread start function to receive a single void* argument - it can point to an object of any data type (POSIX requires void* to also be able to store function pointers).

An example of passing a structure to a thread (without relying on implementation-defined conversion of integers to void* and back):

struct ThreadArgs {
    int thread_num;
    // More data members, as needed.
};

void* apples(void* arg){
    struct ThreadArgs* a = arg;
    printf("Thread: %d\n", a->thread_num);
    free(arg);
    return NULL;
}

int main() {
    pthread_t threads[6];
    struct ThreadArgs* a;
    int apple_num;

    for(apple_num=0; apple_num<6; apple_num++){
        a = malloc(sizeof *a);
        a->thread_num = apple_num;
        pthread_create(&threads[apple_num], NULL, apples, a);
    }
    
    for(apple_num=0; apple_num<6; apple_num++)
        pthread_join(threads[apple_num], NULL);
    
    return 0;
}

Note, that you don't have to allocate the thread arguments structure on the heap (malloc). If you pass an automatic variable (on the stack) to a thread, you must make sure that the variable is unchanged and still exists when the thread accesses it. Allocating the thread arguments structure from the heap is the safest and solves this problem at the expense of malloc/free calls.

Upvotes: 6

Related Questions