Basic Multithread Programming and Synchronization (Futex, Semaphores, Mutex, etc)


Below you’ll find a simple C program that creates 5 worker threads. The threads then wait until the main thread gives them a signal to start the work. I employ different primitives to make this synchronization happen.

Note: Remember that you need to compile this programs with the -pthread GCC option.

Note 2: When you use global variables that will be accessed by different threads you might need to give it the “volatile” qualifier, blocking the compiler to optimize that variable and put it in a register. This means that the variable will always be read from memory and stored on memory after a value change, which is essential to maintain data consistency.

1. Simple busy-wait algorithm with a global variable


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

#define NUM 5

volatile int go = 0;

void* thread_f(void* par){
        int id = (int) par;

        while(go==0);
        printf("Thread %d starting to work!\n",id);
        return NULL;
}

int main(){
        pthread_t threads[NUM];
        int i;

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

        printf("Everyone wait...\n");
        sleep(1);
        printf("Now go!\n");
    go = 1;

    /*give the threads time to complete their tasks*/
        sleep(1);

    printf("Main is quitting...\n");
        return 0;
}

2. Using the Linux Futex Syscalls


#include <stdio.h>
#include <pthread.h>
#include <linux/futex.h>
#include <syscall.h>

#define NUM 5

int futex_addr;

int futex_wait(void* addr, int val1){
  return syscall(SYS_futex,&futex_addr,val1, NULL, NULL, 0);
}
int futex_wake(void* addr, int n){
  return syscall(SYS_futex, addr, FUTEX_WAKE, n, NULL, NULL, 0);
}

void* thread_f(void* par){
        int id = (int) par;
  
    /*go to sleep*/
    futex_addr = 0;
    futex_wait(&futex_addr,0);
        
        printf("Thread %d starting to work!\n",id);
        return NULL;
}

int main(){
        pthread_t threads[NUM];
        int i;

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

        printf("Everyone wait...\n");
        sleep(1);
        printf("Now go!\n");
    /*wake threads*/
    futex_wake(&futex_addr,5);

    /*give the threads time to complete their tasks*/
        sleep(1);

    printf("Main is quitting...\n");
        return 0;
}
< 3. Using Semaphores


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

#define NUM 5

sem_t sem1;

void* thread_f(void* par){
        int id = (int) par;
  
    /*go to sleep*/
    sem_wait(&sem1);
        
        printf("Thread %d starting to work!\n",id);
        return NULL;
}

int main(){
        pthread_t threads[NUM];
        int i;

    sem_init(&sem1, 0, 0);

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

        printf("Everyone wait...\n");
        sleep(1);
        printf("Now go!\n");
    /*wake threads*/
    for(i=0;i<NUM;i++)
      sem_post(&sem1);

    /*give the threads time to complete their tasks*/
        sleep(1);

    printf("Main is quitting...\n");
        return 0;
}

4. Using Mutex Locks

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

#define NUM 5

pthread_mutex_t mutex;

void* thread_f(void* par){
        int id = (int) par;    
        
    /*sleep waiting for the lock*/
    pthread_mutex_lock(&mutex);
        printf("Thread %d starting to work!\n",id);
    pthread_mutex_unlock(&mutex);
        return NULL;
}

int main(){
        pthread_t threads[NUM];
        int i;
    
    /*initialize and lock mutex*/
    pthread_mutex_init(&mutex, NULL);
    pthread_mutex_lock(&mutex);

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

        printf("Everyone wait...\n");
        sleep(1);
        printf("Now go!\n");
    /*wake one thread*/
    pthread_mutex_unlock(&mutex);

    /*give the threads time to complete their tasks*/
        sleep(1);

    printf("Main is quitting...\n");
        return 0;
}

5. Using Mutex and Condition Variables


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

#define NUM 5

pthread_mutex_t mutex;
pthread_cond_t cond;

void* thread_f(void* par){
        int id = (int) par;    
        
    /*sleep waiting for the cond variable*/
    pthread_cond_wait(&cond,&mutex);

    /*wake the next thread*/
    pthread_mutex_unlock(&mutex);
    pthread_cond_signal(&cond);

        printf("Thread %d starting to work!\n",id);
        return NULL;
}

int main(){
        pthread_t threads[NUM];
        int i;
    
    /*initialize mutex and cond variable*/
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&cond,NULL);

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

        printf("Everyone wait...\n");
        sleep(1);
        printf("Now go!\n");
    /*wake one threads*/
    pthread_cond_signal(&cond);

    /*give the threads time to complete their tasks*/
        sleep(1);

    printf("Main is quitting...\n");
  
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);

        return 0;
}

2 thoughts on “Basic Multithread Programming and Synchronization (Futex, Semaphores, Mutex, etc)

  1. MK

    Hello,

    Why would one use the futex calls directly instead of just the mutex + condition variable (which I presume will use futex underneath optimally)

    Thanks for the great blog.

    Reply
  2. Daniel Post author

    @MK, the goal of this program was just to test and try things around, that’s why. I didn’t try to judge the pros and cons of each solution or to measure their performance.

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *