Skip to Content
Course content

135: Threads with threads.h

Click on the "Edit" button in the top corner of the screen to edit your slide content.

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_t mutex 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_join to wait for both worker threads to finish before printing the final total sum to the console.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.