C
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions
-
Section 4: Arrays and Strings
-
Section 5: Pointers
-
Section 6: Memory Management
-
Section 7: Structures and Unions
-
Section 8: The Preprocessor and Build Process
-
Section 9: Standard Library: stdio.h
-
Section 10: Standard Library: stdlib.h
-
Section 11: Standard Library: string.h
-
Section 12: Standard Library: ctype.h and wctype.h
-
Section 13: Standard Library: math.h, complex.h, fenv.h, tgmath.h
-
Section 14: Standard Library: Type and Limit Headers
-
Section 15: Standard Library: Error Handling and Debugging
-
Section 16: Standard Library: Localization and Encoding
-
Section 17: Standard Library: time.h
-
Section 18: Standard Library: Concurrency (C11)
-
Section 19: POSIX and System Programming (unistd.h)
-
Section 20: More Data Structures
-
Section 21: Algorithms in C
-
Section 22: Bitwise Operations
-
Section 23: Command-Line Programs
-
Section 24: Debugging and Best Practices
-
Section 25: Compiler and Language Internals
-
Section 26: Embedded and Cross-Platform Considerations
-
Section 27: Networking Basics
-
Section 28: Practical Projects
-
Section 29: Interview Practice
-
Section 30: C23 Modern Features
-
Section 31: More Practice and Review
135: Threads with threads.h
How do I actually kick off a thread?
Up until now, your C programs have been linear—one thing happens, then the next. To break out of that, we use thrd_create. Think of it as telling the OS, "Hey, start a new execution path right here, and run this specific function."
The tricky part for most people is the function signature. The function you want to run in a thread must return an int and take a single void* as an argument. If you don't match that signature, the compiler will scream at you (or worse, just crash at runtime).
#include <threads.h>
#include <stdio.h>
int run_task(void* arg) {
printf("I'm running in a separate thread!\n");
return 0;
}
int main() {
thrd_t thread;
if (thrd_create(&thread, run_task, NULL) != thrd_success) {
fprintf(stderr, "Failed to create thread\n");
return 1;
}
// We'll talk about joining in a moment...
thrd_join(thread, NULL);
return 0;
}
How do I pass data into that thread function?
Since thrd_create only lets you pass one void*, you can't just throw five different integers into your thread. The professional way to handle this is to wrap your data in a struct. You allocate the struct, fill it with your data, and pass the pointer to that struct.
I've seen a lot of beginners try to pass the address of a local variable that goes out of scope before the thread even starts. Don't do that. Either use a heap-allocated struct or ensure the variable lives long enough for the thread to finish.
typedef struct {
int thread_id;
int limit;
} ThreadData;
int count_to_n(void* arg) {
ThreadData* data = (ThreadData*)arg;
for (int i = 0; i < data->limit; i++) {
// Do some work...
}
printf("Thread %d finished.\n", data->thread_id);
return 0;
}
// In main:
ThreadData data = { .thread_id = 1, .limit = 1000 };
thrd_create(&thread, count_to_n, &data);
How do I stop two threads from stepping on each other's toes?
If you have two threads incrementing the same global variable, you're going to run into a race condition. One thread reads the value, the other increments it, and the first one writes back the old value, effectively "deleting" the other thread's work. This is where mtx_t (the mutex) comes in.
A mutex is basically a "talking stick." If a thread wants to touch a shared variable, it has to lock the mutex first. If another thread already has the lock, the second thread just pauses and waits its turn.
mtx_t lock;
int shared_counter = 0;
int increment_counter(void* arg) {
for (int i = 0; i < 1000; i++) {
mtx_lock(&lock);
shared_counter++;
mtx_unlock(&lock);
}
return 0;
}
int main() {
mtx_init(&lock, mtx_plain);
// ... create threads ...
// ... join threads ...
mtx_destroy(&lock);
}
Why does my program finish before the threads even start?
This is the most common "Why isn't this working?" question I get. The main function is itself a thread. If main reaches return 0;, the entire process dies—including any background threads you started. They don't get to finish; they just vanish.
You use thrd_join to tell the main thread to stop and wait for a specific thread to complete. It's like a rendezvous point. I usually call thrd_join for every thread I create to ensure a clean shutdown and to collect any return values the thread might have sent back.
📋 Practical Task
Build a Parallel Array Summation Tool
Your task is to create a program that calculates the total sum of a large array of integers using two separate threads.
- Create an array of 20,000 integers (filled with any values you like).
- Define a struct that holds a pointer to the array, the starting index, the ending index, and a pointer to a shared total sum variable.
- Spawn two threads: one to sum the first half of the array and one to sum the second half.
- Use a
mtx_tmutex to ensure that when the threads add their local sub-totals to the global shared sum, they don't interfere with each other. - The main thread must use
thrd_jointo wait for both worker threads to finish before printing the final total sum to the console.
There are no comments for now.