Search code examples
cmultithreadingpthreadsmutexrace-condition

Multithreading - Continuing only after ALL threads have finished a task


im trying to implement a multi threading with multible threads (user can enter number of workers = threads when starting the programm) where each thread calling functionA and afterwards functionB. But before functionB should only be executed after ALL threads have called functionA. Thats my pseudo code:

void* worker_do(void* worker_id)
{
  functionA((size_t) worker_id);
  // First thread should only start functionB after ALL threads
  // are finished with functionA
  functionB((size_t) worker_id);
  return NULL;
}

// I am not allowed to change pthread_create and pthread_join here
int main()
{
  // should be a flexible value
  ssize_t num_workers = 20;
  pthread_t* workers  = malloc(num_workers*sizeof(pthread_t));

  for(ssize_t i = 0; i < num_workers; i++)
    pthread_create(&workers[i], NULL, worker_do, (void*) i);

  for(ssize_t i = 0; i < num_workers; i++)
    pthread_join(workers[i], NULL);

  free(workers);

  return 0;
}

I googled and found the possiblity of "condition variables". But I am not sure show they have to implemented for the condition

IF last_thread_has_called_functionA THEN start_calling_fuctionB

Or are condition variables not the right instrument to solve this issue?

Would really appreciate tipps how i can implement that...

bw Robert


Solution

  • Since you asked, to do this with a condition-variable and mutex, you could so something like this:

    #include <stdio.h>
    #include <stdlib.h>
    #include <pthread.h>
    #include <inttypes.h>
    
    #define N_THREADS   10
    
    pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
    pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
    unsigned int count = 0;
    
    void functionA(intptr_t id)
    {
        printf("functionA: %" PRIdPTR "\n", id);
    }
    
    void functionB(intptr_t id)
    {
        printf("functionB: %" PRIdPTR "\n", id);
    }
    
    void* thread_proc(void* pv)
    {
        intptr_t id = (intptr_t)pv;
    
        functionA(id);
    
        // lock the mutex to protect the predicate data (count)
        pthread_mutex_lock(&mtx);
        ++count;
        pthread_cond_broadcast(&cv);
    
        // wait for all threads to finish A
        while (count < N_THREADS)
            pthread_cond_wait(&cv, &mtx);
    
        // this is still owned by us. release it.
        pthread_mutex_unlock(&mtx);
    
        // now B
        functionB(id);
    
        return NULL;
    }
    
    int main()
    {
        pthread_t thrds[N_THREADS];
        for (int i=0; i<N_THREADS; ++i)
            pthread_create(thrds+i, NULL, thread_proc, (void*)(intptr_t)(i+1));
    
        for (int i=0; i<N_THREADS; ++i)
            pthread_join(thrds[i], NULL);
    
        return EXIT_SUCCESS;
    }
    

    Sample Output (varies)

    functionA: 1
    functionA: 4
    functionA: 6
    functionA: 3
    functionA: 2
    functionA: 8
    functionA: 9
    functionA: 7
    functionA: 10
    functionA: 5
    functionB: 10
    functionB: 9
    functionB: 5
    functionB: 7
    functionB: 4
    functionB: 6
    functionB: 1
    functionB: 2
    functionB: 8
    functionB: 3
    

    That said, as Jonathan pointed out in general comment, a barrier is a more elegant solution to this issue. I would post an example, but alas my environment doesn't support them (sad, mac os x). They're available on most Unix pthread implementations, so if your target platform provides them, I suggest investigating them proper.