DEV Community

Cover image for Basics of Multithreading in C
Nathanael Demacon
Nathanael Demacon

Posted on • Edited on

Basics of Multithreading in C

C programs run on a single thread by default - meaning only one instruction is executed at a time. But what if you need to perform multiple tasks simultaneously? For example, a graphical interface must remain responsive even while performing time-consuming operations in the background. This is where multithreading comes in.

Note: Multithreading is different from asynchronous programming, which allows a single thread to handle multiple tasks without blocking.


🧵 What Is a Thread?

A thread is a single sequence of instructions within a process. Each process starts with one thread, but it can create additional threads that run concurrently. These threads share the same memory space, making communication between them efficient, but also introducing risks like race conditions.


šŸ”§ Creating a Thread in C

Let’s dive into how to use threads in C.

On POSIX systems (like Linux), the pthread library provides thread functionality. To use it, you must link your program with -lpthread. For example:

gcc -o main main.c -lpthread 
Enter fullscreen mode Exit fullscreen mode

Here’s a simple program that creates and runs a thread:

#include <pthread.h> #include <unistd.h> #include <stdio.h>  void* wait_fn(void* arg) { sleep(2); printf("Done.\n"); return NULL; } int main() { pthread_t thread; int err = pthread_create(&thread, NULL, wait_fn, NULL); if (err != 0) { printf("An error occurred: %d\n", err); return 1; } printf("Waiting for the thread to end...\n"); pthread_join(thread, NULL); printf("Thread ended.\n"); return 0; } 
Enter fullscreen mode Exit fullscreen mode

Expected Output:

Waiting for the thread to end... Done. Thread ended. 
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • pthread_create starts a new thread and runs the wait_fn function.
  • pthread_join blocks the main thread until the new thread finishes.
  • The sleep() call pauses the thread for 2 seconds to simulate work.

Pretty straightforward, right?


šŸ” Using Mutexes to Prevent Race Conditions

Multithreading introduces the risk of race conditions, especially when multiple threads access shared resources (e.g., a file or a variable). To prevent this, we use a mutex (short for mutual exclusion).

Here’s an example that shows how to safely increment a shared variable:

#include <pthread.h> #include <unistd.h> #include <stdio.h>  pthread_mutex_t lock; int j; void* do_process(void* arg) { pthread_mutex_lock(&lock); int i = 0; j++; while (i < 5) { printf("%d", j); sleep(1); i++; } printf("...Done\n"); pthread_mutex_unlock(&lock); return NULL; } int main(void) { pthread_t t1, t2; if (pthread_mutex_init(&lock, NULL) != 0) { printf("Mutex initialization failed.\n"); return 1; } j = 0; pthread_create(&t1, NULL, do_process, NULL); pthread_create(&t2, NULL, do_process, NULL); pthread_join(t1, NULL); pthread_join(t2, NULL); pthread_mutex_destroy(&lock); return 0; } 
Enter fullscreen mode Exit fullscreen mode

Output:

11111...Done 22222...Done 
Enter fullscreen mode Exit fullscreen mode

The threads run sequentially because the mutex ensures only one thread can access the shared section at a time.

Tip: Even if you switch the order of pthread_join, the output will be the same because the threads themselves are synchronized by the mutex.


🚦 Semaphores: Flexible Synchronization

Semaphores are another synchronization mechanism, similar to mutexes, but more flexible. Unlike mutexes, semaphores don’t have ownership - any thread can lock or unlock them.

This makes semaphores suitable for managing access to a limited set of resources (like a pool of connections or permits).

We won't dive into full semaphore examples here, but if you're interested, check out the semaphore.h library and functions like sem_init, sem_wait, and sem_post.


āœ… Wrapping Up

As you’ve seen, multithreading in C is powerful yet surprisingly approachable. With just a few function calls, you can start writing concurrent programs - but always keep safety in mind with tools like mutexes and semaphores.

Top comments (17)

Collapse
 
mortoray profile image
edA‑qa mort‑ora‑y

pthread is outdated since availability of C11 which introduced standard threading in C. The header files is <threads.h> with functions like thrd_create.

The standard functions for threading, conditions, and signalling, provide guarantees that pthreads cannot.

Collapse
 
quantumsheep profile image
Nathanael Demacon

I have my answers, threads.h is better to use even if it's not POSIX (stackoverflow.com/a/9377007)

Collapse
 
quantumsheep profile image
Nathanael Demacon

pthread.h is POSIX compliant, threads.h isn't.

But sure you can use it, it's implemented in linux and freeBSD kernels.

Collapse
 
noah11012 profile image
Noah11012

But threads.h is C11 compliant so by now ALL compilers have support for C11 at least for the three major ones:

MSVC
GCC
CLANG

Thread Thread
 
quantumsheep profile image
Nathanael Demacon

Yup, seems nice, their is just a lack of documentation, I wanted to know what did it really does.

Thread Thread
 
noah11012 profile image
Noah11012

You can find documentation here: en.cppreference.com/w/c/thread

Thread Thread
 
jl2210 profile image
JL2210

Well, it's the C libraries for two of those platforms that are supposed to implement threads.h (even though I'm pretty sure glibc doesn't), although I'm not sure what Windows does.

Collapse
 
jl2210 profile image
JL2210

It's not the kernels, it's the C libraries. You can have a kernel installed and not be able to do anything without a C library.

Collapse
 
ondrejs profile image
Ondrej

Thank god for every C-related article here (for us, who have interest in low-level programming). Thanks, Nathanael!

Collapse
 
quantumsheep profile image
Nathanael Demacon

C can be a very scary language at first but it's so captivating, I'm happy that you enjoyed reading this article šŸ˜„

Collapse
 
quantumsheep profile image
Nathanael Demacon

Glab that you enjoyed it!

I'm starting to understand a lot about pure Computer Science since I got some courses about the theory of operating systems. It's so fascinating to learn how things really works beyond the compilers and why things are like that in programming languages!

Collapse
 
akahay159 profile image
Akshay Hiremath

Can you suggest that courses pls.

Thread Thread
 
quantumsheep profile image
Nathanael Demacon

For the theory of operating systems, I see this subject at my school (engineering school), but for programming languages I heard that Engineering a Compiler is very great when starting in this domain.

The Dragon Book is very good but much more advanced.

Collapse
 
jl2210 profile image
JL2210

Why can't you use a for loop? for( i = 0; i < 5; i++ )

Collapse
 
quantumsheep profile image
Nathanael Demacon

You can and should do a for loop. It's been 1 year since I did this article, I think it's time to rewrite it with my current knowledge :)

Collapse
 
shreyosghosh profile image
Shreyos Ghosh • Edited

Hey, recently I worked with the C++ standard thread library and made a series about it, where I've talked about the mutexes and locks. I hope you'll find it useful. dev.to/shreyosghosh/series/20850