Search code examples
multithreadingthread-safetypthreadsthread-synchronization

Is there an alternative way to sync


Is there an alternative way to be sure that the threads are ready to recieve the broadcast signal. I want to replace the Sleep(1) function in main.

#include <iostream>
#include <pthread.h>

#define NUM 4


using namespace std;

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_t tid[NUM];


void *threads(void *arg){
    int tid = (int)arg;
    while(true){
        pthread_mutex_lock(&mutex);
        pthread_cond_wait(&cond,&mutex);
        //do some work
        cout<<"Thread: "<<tid<<endl;;
        pthread_mutex_unlock(&mutex);
    }
}

int main(){

    for(int i=0;i<NUM;i++){
        pthread_create(&tid[i],NULL,threads,(void*)i);
    }

    Sleep(1);
    pthread_cond_broadcast(&cond);

    Sleep(1);
    pthread_cond_broadcast(&cond);

    Sleep(1);
    pthread_cond_broadcast(&cond);

    return 0;
}

I tried memory barriers before pthread_cond_wait and i thought of using an counter, but nothing worked for me yet.


Solution

  • Condition variables are usually connected to a predicate. In the other threads, check if predicate is already fulfilled (check while holding the mutex protecting the predicate), if so, do not wait on the condition variable. In main, acquire mutex, change predicate while holding the mutex. Then release mutex and signal or broadcast on the condvar. Here is a similar question: Synchronisation before pthread_cond_broadcast

    Here is some example code:

    #include <iostream>
    #include <pthread.h>
    #include <unistd.h>
    
    #include <cassert>
    
    #define NUM 4
    #define SIZE 256
    
    
    using std::cout;
    
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
    pthread_t tid[NUM];
    
    int work_available;
    
    void *threads(void *arg)
    {
        int tid = *((int*)arg);
        while (1) {
            pthread_mutex_lock(&mutex);
            while (work_available == 0) {
                // While loop since cond_wait can have spurious wakeups.                                                                                                                                                                                                                                                                                                                        
                pthread_cond_wait(&cond, &mutex);
                cout << "Worker " << tid << " woke up...\n";
                cout << "Work available: " << work_available << '\n';
            }
            if (work_available == -1) {
                cout << "Worker " << tid << " quitting\n";
                pthread_mutex_unlock(&mutex); // Easy to forget, better to use C++11 RAII mutexes.                                                                                                                                                                                                                                                                                              
                break;
            }
            assert(work_available > 0);
            work_available--;
            cout << "Worker " << tid << " took one item of work\n";
            pthread_mutex_unlock(&mutex);
    
            //do some work                                                                                                                                                                                                                                                                                                                                                                      
            sleep(2); // simulated work                                                                                                                                                                                                                                                                                                                                                         
            pthread_mutex_lock(&mutex);
            cout << "Worker " << tid << " done with one item of work.\n";
            pthread_mutex_unlock(&mutex);
        }
    }
    
    
    int main()
    {
        work_available = 0;
    
        int args[NUM];
        for (int i=0; i<NUM; i++) {
            args[i] = i;
            pthread_create(&tid[i], NULL, threads, (void*)&args[i]);
        }
    
        const int MAX_TIME = 10;
        for (int i = 0; i < MAX_TIME; i++)
        {
            pthread_mutex_lock(&mutex);
            work_available++;
            cout << "Main thread, work available: " << work_available << '\n';
            pthread_mutex_unlock(&mutex);
            pthread_cond_broadcast(&cond);
            sleep(1);
        }
    
        pthread_mutex_lock(&mutex);
        cout << "Main signalling threads to quit\n";
        work_available = -1;
        pthread_mutex_unlock(&mutex);
        pthread_cond_broadcast(&cond);
    
        for (int i = 0; i < NUM; i++)
        {
            pthread_join(tid[i], NULL);
    
        }
        return 0;
    }