Home > database >  How to execute one thread after another with pthreads?
How to execute one thread after another with pthreads?

Time:01-03

I am working on a simulator for an embedded operating system that uses 5 threads. One acts as the scheduler thread and the other 4 are worker threads. The order of operation is always the same: the scheduler executes between every other thread and goes on activating the one that should go next, like this: SchThread Thread1 SchThread Thread2 SchThread Thread3 SchThread Thread4 SchThread Thread1 ... and so on.

This is currently done in Windows with some bad practices and I'm tasked with switching it to a pthreads implementation. I know this is not how threads are supposed to work because the workload is fully sequential, but the simulator relies on having the 5 threads mentioned.

My question is: how am I supposed to implement this behaviour? Are mutexes or conditions enough? Should I use barriers? Maybe signals from the scheduler to wake up worker threads?

I've seen posts like this one (execution of pthreads in a particular order) that works with 2 threads, but I've failed in my attempts to increase the number to 5. Is using pthread_cond_ a good solution to this problem or am I focusing it wrong?

I've also tried doing the synchronization using mutexes or semaphores unsuccessfully. I've thought of a shared variable that manages the turn but I've been unable to make it work properly.

CodePudding user response:

This is an opinion, because there's more than one way to solve your problem.

I would use a shared global variable, currentThread, whose value identifies which thread should run, and I would use a condition variable to let threads notify each other when currentThread changes.

Sorry I don't have time to write an example at this moment, but you can find examples in the man pages for pthread_cond_t, pthread_cond_wait(...), and pthread_cond_broadcast(...)

CodePudding user response:

I've been thinking on asking this for a few days and the day I post the question I find a possible solution to this problem.

Continuing the explanation of the answer linked in the question, the solution looks like this:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>  /* sleep */

#define MS100 100000
#define HALFS 500000

pthread_mutex_t m_sch;
pthread_cond_t  SCH_GO;
pthread_cond_t  THREAD2_GO;
pthread_cond_t  THREAD3_GO;


unsigned turno = 1;

/* get next turn */
int sig_turno() {
    static int anterior = 0;
    int ret;

    switch (anterior) {
    case 2:
        ret = 3;
        break;
    case 3:
        ret = 2;
        break;
    default: /* first time */
        ret = 2;
        break;
    }

    anterior = ret;
    return ret;
}


int sch_ret = 0;
void *sch(void *arg) {
    int turno_local;

    while (1) {
        pthread_mutex_lock(&m_sch);
        
        while (turno != 1) {
            pthread_cond_wait(&SCH_GO, &m_sch);
        }
        printf("[THREAD SCH]\n");
        usleep(MS100);
        
        turno_local = sig_turno();
        turno = turno_local;
        switch (turno_local) {
        case 2:
            pthread_cond_signal(&THREAD2_GO);
            break;
        case 3:
            pthread_cond_signal(&THREAD3_GO);
            break;
        default:
            printf("error.\n");
            break;
        }
        pthread_mutex_unlock(&m_sch);
    }

    sch_ret = 1;
    return &sch_ret;
}


int thread2_ret = 0;
void *thread2(void *arg) {
    while (1) {
        pthread_mutex_lock(&m_sch);

        while (turno != 2) {
            pthread_cond_wait(&THREAD2_GO, &m_sch);
        }
        printf("[THREAD 2]\n");

        usleep(HALFS);

        turno = 1;

        pthread_cond_signal(&SCH_GO);
        pthread_mutex_unlock(&m_sch);
    }

    thread2_ret = 2;
    return &thread2_ret;
}

int thread3_ret = 0;
void *thread3(void *arg) {
    while (1) {
        pthread_mutex_lock(&m_sch);

        while (turno != 3) {
            pthread_cond_wait(&THREAD3_GO, &m_sch);
        }
        printf("[THREAD 3]\n");

        usleep(HALFS);

        turno = 1;

        pthread_cond_signal(&SCH_GO);
        pthread_mutex_unlock(&m_sch);
    }

    thread3_ret = 3;
    return &thread3_ret;
}

int main() {
    void *ret;

    pthread_t thread_sch, thread_2, thread_3;
    
    pthread_cond_init(&SCH_GO, NULL);
    pthread_cond_init(&THREAD2_GO, NULL);
    pthread_cond_init(&THREAD3_GO, NULL);
    pthread_mutex_init(&m_sch, NULL);

    pthread_create(&thread_sch, NULL, sch, NULL);
    usleep(MS100);
    pthread_create(&thread_2, NULL, thread2, NULL);
    pthread_create(&thread_3, NULL, thread3, NULL);


    pthread_join(thread_sch, &ret);
    pthread_join(thread_2, &ret);
    pthread_join(thread_3, &ret);
    printf("main() ending\n");

    return 0;
}

Where sch is the scheduler's thread.

This can be extended to more threads and works as expected, even though it's just a draft. I've been unable to obtain similar behaviour using only mutexes, so conditions have been the way to go.

I do accept criticism and improvements to this solution, I don't really know if it is correct/good practice.

  • Related