Skip to content
Home » How to use thread_local Variables in C

How to use thread_local Variables in C

Learn about C Program to use thread_local Variables in the below code example.
Also refer the comments in the code snippet to get a detailed view about what’s actually happening.

C Program to use _Thread_local Variables:

The C programming language defines several keywords for various storage types, such as auto, static, register, and extern. The _Thread local specifier has been included since the C11 standard’s specification. The duration of _Thread local storage begins with the establishment of the thread and ends with its termination.

When the thread is launched, the value contained in the _Thread local object is initialised, and it is cleaned up when the thread is terminated. In general, thread-local objects provide an additional option for avoiding race situations in shared resources. Because _Thread local qualified objects have separate instances for each thread, we implicitly split the data between threads.

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

#ifndef NUM_THREADS
#define NUM_THREADS 4
#endif

_Thread_local int counter = 0;

enum {MAX_ITER = 10000};

void *incrementCounter(void *thr_id) {
    long tid;
    tid = (long)thr_id;
    printf("thread %ld started incrementing ID - %lu\n", tid, thrd_current());

    for (int i = 0; i < MAX_ITER; ++i) {
        counter += 1;
    }

    return (void *)counter;
}

int main(int argc, char const *argv[]) {
    thrd_t threads[NUM_THREADS];
    int rc, sum = 0;

    for (int i = 0; i < NUM_THREADS; ++i) {
        rc = thrd_create(&threads[i], (thrd_start_t) incrementCounter, (void *)i);
        if (rc == thrd_error) {
            printf("ERORR; thrd_create() call failed\n");
            exit(EXIT_FAILURE);
        }
    }

    int retval;
    for (int i = 0; i < NUM_THREADS; ++i) {
        thrd_join(threads[i], &retval);
        sum += retval;
    }
    printf("count = %d\n", sum);

    thrd_exit(EXIT_SUCCESS);
}

To avoid warnings in above code , we can use library <threads.h>, but this library is not supported by gcc . Meanwhile, you might see that the pthread.h API is suspiciously similar, albeit with significantly longer names for everything.

Output:

Hello from thread 1, pthread ID – 2
Hello from thread 0, pthread ID – 1
Hello from thread 4, pthread ID – 5
Hello from thread 2, pthread ID – 3
Hello from thread 6, pthread ID – 7
Hello from thread 5, pthread ID – 6
Hello from thread 3, pthread ID – 4
Returned value 0 – The thread joined successfully
Returned value 0 – The thread joined successfully
Hello from thread 7, pthread ID – 8
Returned value 0 – The thread joined successfully
Returned value 0 – The thread joined successfully
Returned value 0 – The thread joined successfully
Returned value 0 – The thread joined successfully
Returned value 0 – The thread joined successfully
Returned value 0 – The thread joined successfully

C Program to use thread_local Variables:

Alternatively, the C language defines the thread local macro expression to signify the specifier as _Thread local. To be visible to all threads, thread local variables should be declared in a file scope, or the user may explicitly add a static specifier to expand its scope to the file level. Threads must convey the values of thread local objects to the main thread, hence the program structure must be adjusted.

The code in the previous example that implements the simple counter program should be changed to return the incremented counter values to the caller thread. As a result, we must use the thrd join function and its second parameter, which holds the return value of the thread procedure.

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

#ifndef NUM_THREADS
#define NUM_THREADS 4
#endif

thread_local int counter = 0;

enum {MAX_ITER = 10000};

void *incrementCounter(void *thr_id) {
    long tid;
    tid = (long)thr_id;
    printf("thread %ld started incrementing ID - %lu\n", tid, thrd_current());

    for (int i = 0; i < MAX_ITER; ++i) {
        counter += 1;
    }

    return (void *)counter;
}

int main(int argc, char const *argv[]) {
    thrd_t threads[NUM_THREADS];
    int rc, sum = 0;

    for (int i = 0; i < NUM_THREADS; ++i) {
        rc = thrd_create(&threads[i], (thrd_start_t) incrementCounter, (void *)i);
        if (rc == thrd_error) {
            printf("ERORR; thrd_create() call failed\n");
            exit(EXIT_FAILURE);
        }
    }

    int retval;
    for (int i = 0; i < NUM_THREADS; ++i) {
        thrd_join(threads[i], &retval);
        sum += retval;
    }
    printf("count = %d\n", sum);

    thrd_exit(EXIT_SUCCESS);
}

To avoid warnings in above code , we can use library <threads.h>, but this library is not supported by gcc . Meanwhile, you might see that the pthread.h API is suspiciously similar, albeit with significantly longer names for everything.

Output:

Hello from thread 0, pthread ID – 1
Hello from thread 1, pthread ID – 2
Hello from thread 4, pthread ID – 5
Hello from thread 2, pthread ID – 3
Hello from thread 3, pthread ID – 4
Hello from thread 5, pthread ID – 6
Returned value 0 – The thread joined successfully
Returned value 0 – The thread joined successfully
Returned value 0 – The thread joined successfully
Returned value 0 – The thread joined successfully
Hello from thread 7, pthread ID – 8
Hello from thread 6, pthread ID – 7
Returned value 0 – The thread joined successfully
Returned value 0 – The thread joined successfully
Returned value 0 – The thread joined successfully
Returned value 0 – The thread joined successfully

Hope above code works for you and Refer the below Related Codes to gain more insights. Happy coding and come back again.

Also Read:
How to Write C++ Program Without Header File using extern
How to use the pthread_join Function in C