In modern computer architecture with multi-core CPUs, multithreading enables high-performance parallel execution. In Linux and POSIX-compliant operating systems, the standard C multithreading API is defined by the POSIX Threads (pthreads) library in the <pthread.h> header.
Figure 1: Single-Threaded vs Multi-Threaded Process: Shared Heap, Data, & Text vs Private Stacks & Registers.
Under the Linux kernel, threads are implemented as Lightweight Processes (LWPs) created via the clone() system call with flags sharing memory space:
Feature |
Heavyweight Process (fork) |
Lightweight Thread (pthreads) |
|---|---|---|
Memory Space |
Completely isolated address space (Copy-on-Write). |
Shares Code, Data, Heap, and File Descriptors. |
Private Resources |
PCB, PID, Page Tables, File Descriptors, Heap, Stack. |
Thread ID (TID), Execution Stack, CPU Registers, Program Counter. |
Creation Cost |
High CPU & RAM overhead (duplicating page tables). |
Very low overhead (up to 30x faster than fork). |
Communication |
Requires IPC (Pipes, FIFOs, Shared Memory, Sockets). |
Direct reading/writing of shared memory & global variables. |
Function |
Purpose |
|---|---|
|
Spawns a new concurrent thread executing a specified start routine function. |
|
Suspends calling thread until the target thread terminates (analogous to |
|
Terminates calling thread and returns a void pointer status to a joining thread. |
|
Returns the unique Thread ID ( |
|
Separates thread so its resources are automatically reclaimed immediately upon exit without joining. |
The program below creates two worker threads that execute simultaneously in background:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
// Thread worker function signature must be: void* (*func)(void*)
void* print_message(void* ptr) {
char* message = (char*) ptr;
for (int i = 1; i <= 3; i++) {
printf("[%s] Running iteration %d (TID: %lu)\n", message, i, (unsigned long)pthread_self());
sleep(1);
}
pthread_exit(NULL);
}
int main() {
pthread_t thread1, thread2;
char msg1[] = "Worker-Alpha";
char msg2[] = "Worker-Beta";
printf("[MAIN] Spawning concurrent threads...\n");
// Syntax: pthread_create(&thread_id, attributes, function_pointer, argument_pointer)
if (pthread_create(&thread1, NULL, print_message, (void*)msg1) != 0) {
perror("Failed to create thread 1");
return 1;
}
if (pthread_create(&thread2, NULL, print_message, (void*)msg2) != 0) {
perror("Failed to create thread 2");
return 2;
}
// Wait for both threads to complete
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
printf("[MAIN] Both worker threads joined successfully. Program exiting.\n");
return 0;
}
$ gcc -Wall -O2 thread_demo.c -pthread -o thread_demo
$ ./thread_demo
Because threads share the same global variables and heap memory, simultaneous write operations can interleave on the CPU registers, causing Race Conditions. For instance, if two threads increment counter++ 100,000 times without synchronization, the final result will be far less than 200,000 due to lost updates!
A Mutex (Mutual Exclusion) lock acts as an electronic key to a Critical Section. Only the single thread holding the lock can enter; all other threads are blocked until the lock is released:
Mutex Function |
Operation |
|---|---|
| Initializes mutex lock with default attributes. |
| Acquires lock. If already locked, thread sleeps until lock is released. |
| Releases lock, waking up any waiting blocked threads. |
| Destroys lock object and frees internal synchronization resources. |
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#define NUM_THREADS 4
#define TRANSACTIONS_PER_THREAD 100000
// Shared resource
long account_balance = 0;
// Mutex lock object
pthread_mutex_t balance_lock;
void* deposit_money(void* arg) {
for (int i = 0; i < TRANSACTIONS_PER_THREAD; i++) {
// --- CRITICAL SECTION BEGIN ---
pthread_mutex_lock(&balance_lock);
account_balance += 10; // Atomically safe deposit of $10
pthread_mutex_unlock(&balance_lock);
// --- CRITICAL SECTION END ---
}
pthread_exit(NULL);
}
int main() {
pthread_t threads[NUM_THREADS];
// Initialize mutex
pthread_mutex_init(&balance_lock, NULL);
printf("[MAIN] Starting %d teller threads...\n", NUM_THREADS);
// Create 4 threads depositing money concurrently
for (int i = 0; i < NUM_THREADS; i++) {
pthread_create(&threads[i], NULL, deposit_money, NULL);
}
// Join all threads
for (int i = 0; i < NUM_THREADS; i++) {
pthread_join(threads[i], NULL);
}
// Expected: 4 * 100,000 * 10 = $4,000,000
printf("[MAIN] All transactions finished!\n");
printf("[MAIN] Final Account Balance: $%ld (Expected: $4000000)\n", account_balance);
// Destroy mutex
pthread_mutex_destroy(&balance_lock);
return 0;
}
Problem: Write a C program that computes the sum of a 1,000,000-element integer array divided equally across 4 threads using pthreads.
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#define SIZE 1000000
#define NUM_THREADS 4
int numbers[SIZE];
long partial_sums[NUM_THREADS];
typedef struct {
int thread_id;
int start_index;
int end_index;
} ThreadData;
void* sum_slice(void* arg) {
ThreadData* data = (ThreadData*) arg;
long local_sum = 0;
for (int i = data->start_index; i < data->end_index; i++) {
local_sum += numbers[i];
}
partial_sums[data->thread_id] = local_sum;
printf("[Thread %d] Summed indices %d to %d: partial_sum = %ld\n",
data->thread_id, data->start_index, data->end_index, local_sum);
pthread_exit(NULL);
}
int main() {
pthread_t threads[NUM_THREADS];
ThreadData thread_data[NUM_THREADS];
int slice_size = SIZE / NUM_THREADS;
// Initialize array with 1s (Expected total sum = 1,000,000)
for (int i = 0; i < SIZE; i++) {
numbers[i] = 1;
}
// Launch threads
for (int t = 0; t < NUM_THREADS; t++) {
thread_data[t].thread_id = t;
thread_data[t].start_index = t * slice_size;
thread_data[t].end_index = (t + 1) * slice_size;
pthread_create(&threads[t], NULL, sum_slice, (void*)&thread_data[t]);
}
// Wait for all worker threads
for (int t = 0; t < NUM_THREADS; t++) {
pthread_join(threads[t], NULL);
}
// Aggregate results
long total_sum = 0;
for (int t = 0; t < NUM_THREADS; t++) {
total_sum += partial_sums[t];
}
printf("==========================================\n");
printf("Total Parallel Array Sum: %ld (Expected: %d)\n", total_sum, SIZE);
printf("==========================================\n");
return 0;
}
-pthread flag links the POSIX thread library (libpthread) and enables thread-safe reentrant standard library definitions (such as _REENTRANT).
pthread_detach(pthread_self()).
Build client-server TCP/IP network communication engines in Class 30: Linux Socket Programming.
Continue to Class 30: Linux Socket Programming → Previous: ← Signals | Full Syllabus