Threading in C/C++ programming language using pthreads

Utpal Kumar   3 minute read      

In this post, we will write a simple C script for performing a task concurrently using the POSIX pthread api in linux/unix like operating systems. For more details on concurrency, see my previous post about parallel computing in Python.

The one mental model

A thread is an independent flow of execution inside the same process — so threads share memory (unlike separate processes). The two calls that matter: pthread_create spawns a worker thread that runs alongside the main thread, and pthread_join makes the main thread wait for the worker to finish before moving on.

Fork-join with pthreads The main thread creates a worker thread with pthread_create; both run concurrently, then pthread_join waits for the worker before main continues. main thread anotherfunc() pthread_create worker thread · myfunc() (8× loop) pthread_join (wait) both run concurrently
The worker runs its 8-iteration loop while the main thread runs anotherfunc(); pthread_join is why main waits for it to finish.

Simple script to make use of the pthread api

#include <pthread.h> //pthread
#include <unistd.h> //sleep
#include <stdio.h> // printf
#include <string.h> //strcpy

struct sData {char text[100];};


void *myfunc(void *voidData) {
    struct sData *data = voidData;
    for (int i=0; i<8; i++) {
        printf ("(i = %d) data text is %s \n", i, data->text);
        sleep(1);
    }
    return NULL;
}

void anotherfunc() {
    for (int i=0; i<3; i++) {
        sleep(1); //sleep for 1s
        printf("(i = %d) this is another func! \n", i);
    }
}

int main() {
    struct sData sd;
    int tret;
    pthread_t tid; // declare a new variable of type pthread_t tid to identify the thread in the system
    strcpy(sd.text, "earthinversion");
    tret = pthread_create(&tid, NULL, myfunc, &sd); // args: pointer to thread id, attributes, function name, arguments to this function
    anotherfunc();
    pthread_join(tid, NULL); 

    return 0;
}

In the main() function above, we define a data sd, and integer tret to store the output of the thread creation. We store text value in the sd.text, which will be argument for the myfunc. Then we create a variable tid of the data type pthread to identify the thread in the system. To create a thread, we need four arguments - pointer to the thread id, attributes (like detached state, scheduling policy, stack address, etc. Here, we specify it to be NULL, hence the default attributes will be used), name of the function and the argument to the function. Here, the function takes only one argument. If multiple arguments need to be given, then we need to use struct.

We then execute function anotherfunc() in the main thread. Since, the function anotherfunc() will finish before myfunc, we need to wait for the myfunc. To do this, we call the pthread_join. The second argument of the pthread_join is the return value of the thread. In this case, we declare it to be NULL.

Compilation

To compile the codes, we need to use the compiler with pthread library.

gcc threading.c -o threading

Add -pthread. To link the threading library and set up thread-safe compilation, the portable, correct command is gcc threading.c -o threading -pthread. Without it you may hit an undefined reference to pthread_create link error. (On very recent glibc, 2.34+, the pthread symbols were merged into libc so it can link without the flag — but keep -pthread for portability.)

When you execute the threading, you should get similar return:

(i = 0) data text is earthinversion 
(i = 1) data text is earthinversion 
(i = 0) this is another func! 
(i = 2) data text is earthinversion 
(i = 1) this is another func! 
(i = 3) data text is earthinversion 
(i = 2) this is another func! 
(i = 4) data text is earthinversion 
(i = 5) data text is earthinversion 
(i = 6) data text is earthinversion 
(i = 7) data text is earthinversion

Notice how the two functions’ lines are interleaved — that’s the concurrency: while the worker thread prints its 8 lines, the main thread is running anotherfunc() at the same time. Once anotherfunc() returns, pthread_join blocks main until the worker’s remaining iterations finish.

Check your understanding

What would happen if you removed the pthread_join(tid, NULL) line?

Recap

Without scrolling up — what’s the threading pattern?

  • A thread runs concurrently inside the same process and shares memory with it.
  • pthread_create(&tid, NULL, fn, arg) launches a worker running fn(arg) (bundle multiple args in a struct).
  • The main thread keeps going (here, anotherfunc()), so output interleaves.
  • pthread_join(tid, NULL) waits for the worker to finish before main returns.
  • Compile with gcc file.c -o out -pthread.

Further reads

  1. Thread functions in C/C++ — GeeksforGeeks.

Disclaimer of liability

The information provided by the Earth Inversion is made available for educational purposes only.

Whilst we endeavor to keep the information up-to-date and correct. Earth Inversion makes no representations or warranties of any kind, express or implied about the completeness, accuracy, reliability, suitability or availability with respect to the website or the information, products, services or related graphics content on the website for any purpose.

UNDER NO CIRCUMSTANCE SHALL WE HAVE ANY LIABILITY TO YOU FOR ANY LOSS OR DAMAGE OF ANY KIND INCURRED AS A RESULT OF THE USE OF THE SITE OR RELIANCE ON ANY INFORMATION PROVIDED ON THE SITE. ANY RELIANCE YOU PLACED ON SUCH MATERIAL IS THEREFORE STRICTLY AT YOUR OWN RISK.


Leave a comment