google-site-verification: google61fe8ba583a51912.html
Multithreading with pthreads in C: Complete Guide

Introduction to POSIX Threads (pthreads)

POSIX threads (pthreads) is a standardized C language threading API for Unix-like operating systems. It provides a powerful interface for creating and managing multiple threads of execution within a single process, enabling parallel processing and improved performance on multi-core systems.


Threading Architecture Overview

Multithreading Architecture ├── Thread Management │ ├── Creation (pthread_create) │ ├── Termination (pthread_exit) │ ├── Joining (pthread_join) │ └── Detaching (pthread_detach) ├── Synchronization │ ├── Mutexes (pthread_mutex) │ ├── Condition Variables │ ├── Read-Write Locks │ ├── Barriers │ └── Semaphores └── Thread-local Storage ├── Thread-specific data └── Thread-local variables

Thread States

 Created ↓ Runnable ←→ Running ↓ ↓ Blocked Terminated

Basic Thread Operations

1. Creating and Joining Threads

#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h> // Thread function void* thread_function(void* arg) { int thread_num = *(int*)arg; for (int i = 0; i < 5; i++) { printf("Thread %d: iteration %d\n", thread_num, i); sleep(1); // Simulate work } printf("Thread %d finished\n", thread_num); return NULL; } int main() { pthread_t thread1, thread2; int t1_arg = 1, t2_arg = 2; printf("Main: Creating threads\n"); // Create threads if (pthread_create(&thread1, NULL, thread_function, &t1_arg) != 0) { perror("Failed to create thread 1"); return 1; } if (pthread_create(&thread2, NULL, thread_function, &t2_arg) != 0) { perror("Failed to create thread 2"); return 1; } printf("Main: Waiting for threads to finish\n"); // Wait for threads to complete pthread_join(thread1, NULL); pthread_join(thread2, NULL); printf("Main: All threads finished\n"); return 0; }

Compilation:

gcc -pthread threads.c -o threads ./threads

2. Thread with Return Values

#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h> typedef struct { int start; int end; int result; } ThreadData; void* sum_range(void* arg) { ThreadData* data = (ThreadData*)arg; data->result = 0; printf("Thread computing sum from %d to %d\n", data->start, data->end); for (int i = data->start; i <= data->end; i++) { data->result += i; usleep(1000); // Small delay to simulate work } printf("Thread finished: sum = %d\n", data->result); return (void*)&data->result; } int main() { pthread_t thread1, thread2; ThreadData data1 = {1, 50, 0}; ThreadData data2 = {51, 100, 0}; // Create threads pthread_create(&thread1, NULL, sum_range, &data1); pthread_create(&thread2, NULL, sum_range, &data2); // Wait for threads and get results int* result1; int* result2; pthread_join(thread1, (void**)&result1); pthread_join(thread2, (void**)&result2); int total = *result1 + *result2; printf("\nMain: Partial sums: %d + %d = %d\n", *result1, *result2, total); printf("Main: Expected sum 1-100 = 5050\n"); return 0; }

3. Thread Arguments and Context

#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <string.h> #include <unistd.h> typedef struct { char name[50]; int id; int iterations; int delay_ms; } ThreadContext; void* worker_thread(void* arg) { ThreadContext* ctx = (ThreadContext*)arg; for (int i = 0; i < ctx->iterations; i++) { printf("[%s-%d] Working: iteration %d\n", ctx->name, ctx->id, i + 1); usleep(ctx->delay_ms * 1000); } printf("[%s-%d] Finished\n", ctx->name, ctx->id); // Create result string (must persist after function returns) char* result = malloc(100); sprintf(result, "%s-%d completed %d iterations", ctx->name, ctx->id, ctx->iterations); return result; } int main() { pthread_t threads[3]; ThreadContext contexts[3]; // Initialize thread contexts strcpy(contexts[0].name, "Worker"); contexts[0].id = 1; contexts[0].iterations = 3; contexts[0].delay_ms = 500; strcpy(contexts[1].name, "Helper"); contexts[1].id = 2; contexts[1].iterations = 5; contexts[1].delay_ms = 300; strcpy(contexts[2].name, "Daemon"); contexts[2].id = 3; contexts[2].iterations = 2; contexts[2].delay_ms = 800; printf("Main: Creating threads\n"); for (int i = 0; i < 3; i++) { pthread_create(&threads[i], NULL, worker_thread, &contexts[i]); } printf("Main: Waiting for threads\n"); // Collect results for (int i = 0; i < 3; i++) { char* result; pthread_join(threads[i], (void**)&result); printf("Thread %d result: %s\n", i + 1, result); free(result); } printf("Main: All done\n"); return 0; }

Thread Synchronization

1. Mutex Basics

#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h> #define NUM_THREADS 5 #define NUM_ITERATIONS 100000 // Shared resource long long counter = 0; pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // Thread function with mutex protection void* increment_with_mutex(void* arg) { int thread_id = *(int*)arg; for (int i = 0; i < NUM_ITERATIONS; i++) { pthread_mutex_lock(&mutex); counter++; // Critical section pthread_mutex_unlock(&mutex); } printf("Thread %d finished\n", thread_id); return NULL; } // Thread function without mutex (race condition demo) void* increment_without_mutex(void* arg) { int thread_id = *(int*)arg; for (int i = 0; i < NUM_ITERATIONS; i++) { counter++; // RACE CONDITION! } printf("Thread %d finished (unsafe)\n", thread_id); return NULL; } int main() { pthread_t threads[NUM_THREADS]; int thread_ids[NUM_THREADS]; printf("=== Mutex Protection Demo ===\n\n"); // Test with mutex counter = 0; printf("With mutex protection:\n"); for (int i = 0; i < NUM_THREADS; i++) { thread_ids[i] = i; pthread_create(&threads[i], NULL, increment_with_mutex, &thread_ids[i]); } for (int i = 0; i < NUM_THREADS; i++) { pthread_join(threads[i], NULL); } printf("Final counter: %lld (expected: %d)\n\n", counter, NUM_THREADS * NUM_ITERATIONS); // Test without mutex counter = 0; printf("Without mutex protection (race condition):\n"); for (int i = 0; i < NUM_THREADS; i++) { pthread_create(&threads[i], NULL, increment_without_mutex, &thread_ids[i]); } for (int i = 0; i < NUM_THREADS; i++) { pthread_join(threads[i], NULL); } printf("Final counter: %lld (expected: %d)\n", counter, NUM_THREADS * NUM_ITERATIONS); pthread_mutex_destroy(&mutex); return 0; }

2. Mutex with Error Handling

#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <errno.h> typedef struct { pthread_mutex_t mutex; int* data; int size; int count; } SafeArray; SafeArray* create_safe_array(int size) { SafeArray* arr = malloc(sizeof(SafeArray)); if (!arr) return NULL; // Initialize mutex with attributes pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK); if (pthread_mutex_init(&arr->mutex, &attr) != 0) { free(arr); return NULL; } arr->data = calloc(size, sizeof(int)); arr->size = size; arr->count = 0; pthread_mutexattr_destroy(&attr); return arr; } int safe_array_add(SafeArray* arr, int value) { int ret = pthread_mutex_lock(&arr->mutex); if (ret != 0) { errno = ret; return -1; } if (arr->count >= arr->size) { pthread_mutex_unlock(&arr->mutex); return -1; // Array full } arr->data[arr->count++] = value; pthread_mutex_unlock(&arr->mutex); return 0; } int safe_array_get(SafeArray* arr, int index, int* value) { if (index < 0 || index >= arr->size) { return -1; } pthread_mutex_lock(&arr->mutex); if (index >= arr->count) { pthread_mutex_unlock(&arr->mutex); return -1; // Element not yet added } *value = arr->data[index]; pthread_mutex_unlock(&arr->mutex); return 0; } void safe_array_destroy(SafeArray* arr) { if (arr) { pthread_mutex_destroy(&arr->mutex); free(arr->data); free(arr); } } void* producer_thread(void* arg) { SafeArray* arr = (SafeArray*)arg; for (int i = 0; i < 10; i++) { if (safe_array_add(arr, i * 10) == 0) { printf("Producer added: %d\n", i * 10); } else { printf("Producer failed to add %d (array full)\n", i * 10); } usleep(100000); // 100ms delay } return NULL; } void* consumer_thread(void* arg) { SafeArray* arr = (SafeArray*)arg; int value; for (int i = 0; i < 10; i++) { if (safe_array_get(arr, i, &value) == 0) { printf("Consumer got: %d\n", value); } else { printf("Consumer failed to get element %d\n", i); } usleep(150000); // 150ms delay } return NULL; } int main() { SafeArray* arr = create_safe_array(10); if (!arr) { printf("Failed to create safe array\n"); return 1; } pthread_t producer, consumer; pthread_create(&producer, NULL, producer_thread, arr); pthread_create(&consumer, NULL, consumer_thread, arr); pthread_join(producer, NULL); pthread_join(consumer, NULL); safe_array_destroy(arr); return 0; }

3. Condition Variables

#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h> #define BUFFER_SIZE 5 typedef struct { int buffer[BUFFER_SIZE]; int count; int in; int out; pthread_mutex_t mutex; pthread_cond_t not_full; pthread_cond_t not_empty; } BoundedBuffer; void buffer_init(BoundedBuffer* buf) { buf->count = 0; buf->in = 0; buf->out = 0; pthread_mutex_init(&buf->mutex, NULL); pthread_cond_init(&buf->not_full, NULL); pthread_cond_init(&buf->not_empty, NULL); } void buffer_destroy(BoundedBuffer* buf) { pthread_mutex_destroy(&buf->mutex); pthread_cond_destroy(&buf->not_full); pthread_cond_destroy(&buf->not_empty); } void buffer_put(BoundedBuffer* buf, int item) { pthread_mutex_lock(&buf->mutex); // Wait while buffer is full while (buf->count == BUFFER_SIZE) { printf("Buffer full, producer waiting...\n"); pthread_cond_wait(&buf->not_full, &buf->mutex); } // Add item to buffer buf->buffer[buf->in] = item; buf->in = (buf->in + 1) % BUFFER_SIZE; buf->count++; printf("Produced: %d (count: %d)\n", item, buf->count); // Signal that buffer is not empty pthread_cond_signal(&buf->not_empty); pthread_mutex_unlock(&buf->mutex); } int buffer_get(BoundedBuffer* buf) { pthread_mutex_lock(&buf->mutex); // Wait while buffer is empty while (buf->count == 0) { printf("Buffer empty, consumer waiting...\n"); pthread_cond_wait(&buf->not_empty, &buf->mutex); } // Get item from buffer int item = buf->buffer[buf->out]; buf->out = (buf->out + 1) % BUFFER_SIZE; buf->count--; printf("Consumed: %d (count: %d)\n", item, buf->count); // Signal that buffer is not full pthread_cond_signal(&buf->not_full); pthread_mutex_unlock(&buf->mutex); return item; } void* producer(void* arg) { BoundedBuffer* buf = (BoundedBuffer*)arg; for (int i = 0; i < 20; i++) { buffer_put(buf, i); usleep(rand() % 500000); // Random delay 0-500ms } return NULL; } void* consumer(void* arg) { BoundedBuffer* buf = (BoundedBuffer*)arg; for (int i = 0; i < 20; i++) { int item = buffer_get(buf); usleep(rand() % 800000); // Random delay 0-800ms } return NULL; } int main() { srand(time(NULL)); BoundedBuffer buffer; buffer_init(&buffer); pthread_t prod_thread, cons_thread; printf("Starting producer-consumer demo (buffer size: %d)\n\n", BUFFER_SIZE); pthread_create(&prod_thread, NULL, producer, &buffer); pthread_create(&cons_thread, NULL, consumer, &buffer); pthread_join(prod_thread, NULL); pthread_join(cons_thread, NULL); buffer_destroy(&buffer); printf("\nProducer-consumer finished\n"); return 0; }

4. Read-Write Locks

#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h> typedef struct { int data; int readers_active; int writers_active; pthread_rwlock_t rwlock; } SharedData; void init_data(SharedData* sd) { sd->data = 0; sd->readers_active = 0; sd->writers_active = 0; pthread_rwlock_init(&sd->rwlock, NULL); } void destroy_data(SharedData* sd) { pthread_rwlock_destroy(&sd->rwlock); } void* reader_thread(void* arg) { SharedData* sd = (SharedData*)arg; int thread_id = rand() % 100; for (int i = 0; i < 5; i++) { // Acquire read lock pthread_rwlock_rdlock(&sd->rwlock); // Reading sd->readers_active++; printf("Reader %d: reading value %d (readers: %d, writers: %d)\n", thread_id, sd->data, sd->readers_active, sd->writers_active); sd->readers_active--; // Release read lock pthread_rwlock_unlock(&sd->rwlock); usleep(rand() % 500000); } return NULL; } void* writer_thread(void* arg) { SharedData* sd = (SharedData*)arg; int thread_id = rand() % 100; for (int i = 0; i < 3; i++) { // Acquire write lock pthread_rwlock_wrlock(&sd->rwlock); // Writing sd->writers_active++; sd->data += 10; printf("Writer %d: writing value %d (readers: %d, writers: %d)\n", thread_id, sd->data, sd->readers_active, sd->writers_active); sd->writers_active--; // Release write lock pthread_rwlock_unlock(&sd->rwlock); usleep(rand() % 800000); } return NULL; } int main() { srand(time(NULL)); SharedData sd; init_data(&sd); pthread_t readers[5], writers[2]; printf("=== Read-Write Lock Demo ===\n\n"); // Create reader threads for (int i = 0; i < 5; i++) { pthread_create(&readers[i], NULL, reader_thread, &sd); } // Create writer threads for (int i = 0; i < 2; i++) { pthread_create(&writers[i], NULL, writer_thread, &sd); } // Wait for all threads for (int i = 0; i < 5; i++) { pthread_join(readers[i], NULL); } for (int i = 0; i < 2; i++) { pthread_join(writers[i], NULL); } printf("\nFinal value: %d\n", sd.data); destroy_data(&sd); return 0; }

Advanced Threading Patterns

1. Thread Pool

#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h> #define THREAD_POOL_SIZE 4 #define TASK_QUEUE_SIZE 20 typedef struct { void (*function)(void*); void* argument; } Task; typedef struct { Task task_queue[TASK_QUEUE_SIZE]; int queue_size; int head; int tail; pthread_mutex_t mutex; pthread_cond_t not_empty; pthread_cond_t not_full; pthread_t threads[THREAD_POOL_SIZE]; int shutdown; } ThreadPool; ThreadPool* thread_pool_create() { ThreadPool* pool = malloc(sizeof(ThreadPool)); pool->queue_size = 0; pool->head = 0; pool->tail = 0; pool->shutdown = 0; pthread_mutex_init(&pool->mutex, NULL); pthread_cond_init(&pool->not_empty, NULL); pthread_cond_init(&pool->not_full, NULL); return pool; } void* worker_thread(void* arg) { ThreadPool* pool = (ThreadPool*)arg; while (1) { pthread_mutex_lock(&pool->mutex); // Wait while queue is empty and not shutting down while (pool->queue_size == 0 && !pool->shutdown) { pthread_cond_wait(&pool->not_empty, &pool->mutex); } if (pool->shutdown) { pthread_mutex_unlock(&pool->mutex); break; } // Get task from queue Task task = pool->task_queue[pool->head]; pool->head = (pool->head + 1) % TASK_QUEUE_SIZE; pool->queue_size--; pthread_cond_signal(&pool->not_full); pthread_mutex_unlock(&pool->mutex); // Execute task task.function(task.argument); } return NULL; } void thread_pool_start(ThreadPool* pool) { for (int i = 0; i < THREAD_POOL_SIZE; i++) { pthread_create(&pool->threads[i], NULL, worker_thread, pool); } } int thread_pool_submit(ThreadPool* pool, void (*function)(void*), void* arg) { pthread_mutex_lock(&pool->mutex); // Wait if queue is full while (pool->queue_size == TASK_QUEUE_SIZE) { pthread_cond_wait(&pool->not_full, &pool->mutex); } // Add task to queue pool->task_queue[pool->tail].function = function; pool->task_queue[pool->tail].argument = arg; pool->tail = (pool->tail + 1) % TASK_QUEUE_SIZE; pool->queue_size++; pthread_cond_signal(&pool->not_empty); pthread_mutex_unlock(&pool->mutex); return 0; } void thread_pool_shutdown(ThreadPool* pool) { pthread_mutex_lock(&pool->mutex); pool->shutdown = 1; pthread_cond_broadcast(&pool->not_empty); pthread_mutex_unlock(&pool->mutex); for (int i = 0; i < THREAD_POOL_SIZE; i++) { pthread_join(pool->threads[i], NULL); } pthread_mutex_destroy(&pool->mutex); pthread_cond_destroy(&pool->not_empty); pthread_cond_destroy(&pool->not_full); free(pool); } // Example task functions void print_task(void* arg) { int* num = (int*)arg; printf("Task %d executed by thread %ld\n", *num, pthread_self()); free(arg); } void compute_task(void* arg) { int* n = (int*)arg; int result = 0; for (int i = 0; i <= *n; i++) { result += i; } printf("Sum 0-%d = %d (thread %ld)\n", *n, result, pthread_self()); free(arg); } int main() { ThreadPool* pool = thread_pool_create(); thread_pool_start(pool); printf("=== Thread Pool Demo ===\n"); printf("Pool size: %d, Queue size: %d\n\n", THREAD_POOL_SIZE, TASK_QUEUE_SIZE); // Submit tasks for (int i = 0; i < 15; i++) { int* num = malloc(sizeof(int)); *num = i + 1; thread_pool_submit(pool, print_task, num); } for (int i = 0; i < 5; i++) { int* num = malloc(sizeof(int)); *num = i * 10; thread_pool_submit(pool, compute_task, num); } sleep(2); // Let tasks complete thread_pool_shutdown(pool); return 0; }

2. Barrier Synchronization

#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h> #define NUM_THREADS 5 #define NUM_PHASES 3 typedef struct { pthread_barrier_t barrier; int phase_data[NUM_THREADS]; } SharedContext; void* worker_phase(void* arg) { SharedContext* ctx = (SharedContext*)arg; long thread_id = (long)arg % NUM_THREADS; for (int phase = 0; phase < NUM_PHASES; phase++) { // Phase 1: Compute printf("Thread %ld: Starting phase %d\n", thread_id, phase); usleep(rand() % 500000); // Random work ctx->phase_data[thread_id] = rand() % 100; printf("Thread %ld: Completed phase %d, data = %d\n", thread_id, phase, ctx->phase_data[thread_id]); // Wait for all threads to complete phase pthread_barrier_wait(&ctx->barrier); // Phase 2: Process results (only one thread does this) if (thread_id == 0) { int sum = 0; for (int i = 0; i < NUM_THREADS; i++) { sum += ctx->phase_data[i]; } printf("Phase %d total sum: %d\n", phase, sum); } // Wait again before next phase pthread_barrier_wait(&ctx->barrier); } return NULL; } int main() { SharedContext ctx; pthread_t threads[NUM_THREADS]; // Initialize barrier pthread_barrier_init(&ctx.barrier, NULL, NUM_THREADS); printf("=== Barrier Synchronization Demo ===\n"); printf("%d threads, %d phases\n\n", NUM_THREADS, NUM_PHASES); // Create threads for (long i = 0; i < NUM_THREADS; i++) { pthread_create(&threads[i], NULL, worker_phase, (void*)i); } // Wait for threads for (int i = 0; i < NUM_THREADS; i++) { pthread_join(threads[i], NULL); } pthread_barrier_destroy(&ctx.barrier); printf("\nAll phases completed\n"); return 0; }

3. Thread-local Storage

#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h> // Thread-local storage using __thread keyword __thread int thread_local_counter = 0; // Thread-specific data using pthread_key_t pthread_key_t thread_specific_key; typedef struct { char name[50]; int id; } ThreadData; void destructor(void* value) { if (value) { ThreadData* data = (ThreadData*)value; printf("Destructor: Cleaning up thread %d (%s)\n", data->id, data->name); free(value); } } void* thread_function(void* arg) { long thread_id = (long)arg; // Using __thread local storage thread_local_counter++; printf("Thread %ld: thread_local_counter = %d\n", thread_id, thread_local_counter); // Using pthread_key_t thread-specific data ThreadData* data = malloc(sizeof(ThreadData)); sprintf(data->name, "Thread-%ld", thread_id); data->id = thread_id; pthread_setspecific(thread_specific_key, data); // Retrieve and use thread-specific data ThreadData* retrieved = pthread_getspecific(thread_specific_key); printf("Thread %ld: Retrieved data - name: %s, id: %d\n", thread_id, retrieved->name, retrieved->id); sleep(1); return NULL; } int main() { pthread_t threads[5]; // Create thread-specific data key pthread_key_create(&thread_specific_key, destructor); printf("=== Thread-Local Storage Demo ===\n\n"); // Create threads for (long i = 0; i < 5; i++) { pthread_create(&threads[i], NULL, thread_function, (void*)i); } // Wait for threads for (int i = 0; i < 5; i++) { pthread_join(threads[i], NULL); } // Delete key pthread_key_delete(thread_specific_key); return 0; }

Common Pitfalls and Solutions

1. Deadlock Example

#include <stdio.h> #include <pthread.h> #include <unistd.h> pthread_mutex_t mutex_a = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t mutex_b = PTHREAD_MUTEX_INITIALIZER; // DEADLOCK VERSION - DON'T DO THIS void* thread1_deadlock(void* arg) { printf("Thread 1: Locking A\n"); pthread_mutex_lock(&mutex_a); sleep(1); printf("Thread 1: Trying to lock B\n"); pthread_mutex_lock(&mutex_b); // DEADLOCK! printf("Thread 1: Got both locks\n"); pthread_mutex_unlock(&mutex_b); pthread_mutex_unlock(&mutex_a); return NULL; } void* thread2_deadlock(void* arg) { printf("Thread 2: Locking B\n"); pthread_mutex_lock(&mutex_b); sleep(1); printf("Thread 2: Trying to lock A\n"); pthread_mutex_lock(&mutex_a); // DEADLOCK! printf("Thread 2: Got both locks\n"); pthread_mutex_unlock(&mutex_a); pthread_mutex_unlock(&mutex_b); return NULL; } // SOLUTION - Consistent lock ordering void* thread1_safe(void* arg) { printf("Thread 1 (safe): Locking A then B\n"); pthread_mutex_lock(&mutex_a); pthread_mutex_lock(&mutex_b); printf("Thread 1: Working...\n"); sleep(1); pthread_mutex_unlock(&mutex_b); pthread_mutex_unlock(&mutex_a); return NULL; } void* thread2_safe(void* arg) { printf("Thread 2 (safe): Locking A then B (same order)\n"); pthread_mutex_lock(&mutex_a); // Same order as thread1 pthread_mutex_lock(&mutex_b); printf("Thread 2: Working...\n"); sleep(1); pthread_mutex_unlock(&mutex_b); pthread_mutex_unlock(&mutex_a); return NULL; } int main() { pthread_t t1, t2; printf("=== Deadlock Demo ===\n"); printf("(Deadlock version commented out)\n\n"); // Uncomment to see deadlock /* printf("Creating deadlock threads...\n"); pthread_create(&t1, NULL, thread1_deadlock, NULL); pthread_create(&t2, NULL, thread2_deadlock, NULL); */ printf("Creating safe threads (consistent lock ordering)...\n"); pthread_create(&t1, NULL, thread1_safe, NULL); pthread_create(&t2, NULL, thread2_safe, NULL); pthread_join(t1, NULL); pthread_join(t2, NULL); printf("\nSafe version completed successfully\n"); return 0; }

2. Race Condition Detection

#include <stdio.h> #include <pthread.h> #include <assert.h> #define NUM_THREADS 10 #define NUM_ITERATIONS 10000 // Shared counter with race condition int shared_counter = 0; // Protected counter int protected_counter = 0; pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // Atomic counter (using GCC atomic builtins) int atomic_counter = 0; void* race_thread(void* arg) { for (int i = 0; i < NUM_ITERATIONS; i++) { shared_counter++; // RACE CONDITION } return NULL; } void* protected_thread(void* arg) { for (int i = 0; i < NUM_ITERATIONS; i++) { pthread_mutex_lock(&mutex); protected_counter++; pthread_mutex_unlock(&mutex); } return NULL; } void* atomic_thread(void* arg) { for (int i = 0; i < NUM_ITERATIONS; i++) { __sync_fetch_and_add(&atomic_counter, 1); } return NULL; } void run_test(void* (*thread_func)(void*), const char* name) { pthread_t threads[NUM_THREADS]; if (thread_func == race_thread) { shared_counter = 0; } else if (thread_func == protected_thread) { protected_counter = 0; } else { atomic_counter = 0; } printf("\nTesting %s:\n", name); for (int i = 0; i < NUM_THREADS; i++) { pthread_create(&threads[i], NULL, thread_func, NULL); } for (int i = 0; i < NUM_THREADS; i++) { pthread_join(threads[i], NULL); } int expected = NUM_THREADS * NUM_ITERATIONS; int actual; if (thread_func == race_thread) { actual = shared_counter; } else if (thread_func == protected_thread) { actual = protected_counter; } else { actual = atomic_counter; } printf(" Expected: %d\n", expected); printf(" Actual: %d\n", actual); printf(" Difference: %d\n", expected - actual); } int main() { printf("=== Race Condition Detection ===\n"); printf("Threads: %d, Iterations per thread: %d\n", NUM_THREADS, NUM_ITERATIONS); run_test(race_thread, "Race condition (unsafe)"); run_test(protected_thread, "Mutex protected (safe)"); run_test(atomic_thread, "Atomic operations (safe)"); pthread_mutex_destroy(&mutex); return 0; }

Performance Benchmarking

#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <time.h> #define NUM_THREADS 4 #define ARRAY_SIZE 10000000 #define NUM_RUNS 5 typedef struct { int* array; int start; int end; long long result; } ThreadWork; void* sum_array_worker(void* arg) { ThreadWork* work = (ThreadWork*)arg; work->result = 0; for (int i = work->start; i < work->end; i++) { work->result += work->array[i]; } return NULL; } long long parallel_sum(int* array, int size, int num_threads) { pthread_t threads[num_threads]; ThreadWork works[num_threads]; int chunk_size = size / num_threads; // Create threads for (int i = 0; i < num_threads; i++) { works[i].array = array; works[i].start = i * chunk_size; works[i].end = (i == num_threads - 1) ? size : (i + 1) * chunk_size; works[i].result = 0; pthread_create(&threads[i], NULL, sum_array_worker, &works[i]); } // Wait for threads and sum results long long total = 0; for (int i = 0; i < num_threads; i++) { pthread_join(threads[i], NULL); total += works[i].result; } return total; } long long sequential_sum(int* array, int size) { long long total = 0; for (int i = 0; i < size; i++) { total += array[i]; } return total; } double get_time() { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return ts.tv_sec + ts.tv_nsec / 1e9; } int main() { // Initialize array int* array = malloc(ARRAY_SIZE * sizeof(int)); for (int i = 0; i < ARRAY_SIZE; i++) { array[i] = rand() % 100; } printf("=== Performance Benchmark ===\n"); printf("Array size: %d\n", ARRAY_SIZE); printf("Threads: %d\n", NUM_THREADS); printf("Runs per test: %d\n\n", NUM_RUNS); // Sequential sum benchmark double seq_time = 0; long long seq_result = 0; for (int run = 0; run < NUM_RUNS; run++) { double start = get_time(); seq_result = sequential_sum(array, ARRAY_SIZE); double end = get_time(); seq_time += (end - start); } seq_time /= NUM_RUNS; // Parallel sum benchmark double par_time = 0; long long par_result = 0; for (int run = 0; run < NUM_RUNS; run++) { double start = get_time(); par_result = parallel_sum(array, ARRAY_SIZE, NUM_THREADS); double end = get_time(); par_time += (end - start); } par_time /= NUM_RUNS; printf("Sequential sum:\n"); printf(" Result: %lld\n", seq_result); printf(" Average time: %.4f seconds\n", seq_time); printf("\nParallel sum (%d threads):\n", NUM_THREADS); printf(" Result: %lld\n", par_result); printf(" Average time: %.4f seconds\n", par_time); printf(" Speedup: %.2fx\n", seq_time / par_time); // Verify results if (seq_result == par_result) { printf("\n✓ Results match\n"); } else { printf("\n✗ Results differ!\n"); printf(" Sequential: %lld\n", seq_result); printf(" Parallel: %lld\n", par_result); } free(array); return 0; }

Best Practices Summary

Do's and Don'ts

// ✅ DO: Initialize synchronization primitives pthread_mutex_init(&mutex, NULL); pthread_cond_init(&cond, NULL); // ✅ DO: Always lock before accessing shared data pthread_mutex_lock(&mutex); shared_counter++; pthread_mutex_unlock(&mutex); // ✅ DO: Check return values if (pthread_create(&thread, NULL, func, arg) != 0) { perror("Failed to create thread"); } // ✅ DO: Join or detach threads pthread_join(thread, NULL); // Wait for thread // OR pthread_detach(thread); // Let thread clean up itself // ✅ DO: Use condition variables for signaling while (condition) { pthread_cond_wait(&cond, &mutex); } // ✅ DO: Destroy synchronization objects when done pthread_mutex_destroy(&mutex); pthread_cond_destroy(&cond); // ❌ DON'T: Forget to unlock mutex pthread_mutex_lock(&mutex); // ... do work // Missing unlock! - will cause deadlock // ❌ DON'T: Access shared data without locking shared_data++; // RACE CONDITION! // ❌ DON'T: Hold locks while sleeping pthread_mutex_lock(&mutex); sleep(10); // Blocks other threads unnecessarily pthread_mutex_unlock(&mutex); // ❌ DON'T: Use different lock ordering // Thread 1: lock A then B // Thread 2: lock B then A // DEADLOCK! // ❌ DON'T: Signal condition without holding mutex pthread_cond_signal(&cond); // Should hold mutex

Common pthread Functions Reference

FunctionPurpose
pthread_create()Create a new thread
pthread_join()Wait for thread termination
pthread_exit()Terminate calling thread
pthread_self()Get thread ID
pthread_mutex_lock()Lock mutex
pthread_mutex_unlock()Unlock mutex
pthread_cond_wait()Wait on condition
pthread_cond_signal()Signal one waiting thread
pthread_cond_broadcast()Signal all waiting threads
pthread_rwlock_rdlock()Acquire read lock
pthread_rwlock_wrlock()Acquire write lock
pthread_barrier_wait()Synchronize at barrier
pthread_key_create()Create thread-specific data key

Conclusion

Multithreading with pthreads enables powerful parallel programming in C:

Key Concepts

  • Thread creation and management - Create, join, detach threads
  • Synchronization - Mutexes, condition variables, read-write locks
  • Thread safety - Protect shared data from race conditions
  • Deadlock prevention - Consistent lock ordering
  • Performance - Leverage multiple cores for parallel processing

Best Practices

  1. Always synchronize access to shared data
  2. Use consistent lock ordering to prevent deadlocks
  3. Keep critical sections as small as possible
  4. Check return values from pthread functions
  5. Join or detach all created threads
  6. Destroy synchronization objects when done
  7. Consider thread safety in library design
  8. Profile and benchmark for optimal performance

Common Applications

  • Parallel computation and data processing
  • Server applications handling multiple clients
  • GUI applications with background tasks
  • Real-time systems with concurrent operations
  • Producer-consumer problems

Mastering pthreads is essential for writing efficient, scalable, and responsive applications in C on Unix-like systems.

Complete C Programming Guide + Compilers Collection


1. C srand() Function – Understanding Seed Initialization

https://macronepal.com/understanding-the-c-srand-function
Explains how srand() initializes the pseudo-random number generator in C by setting a seed value. Using the same seed produces the same sequence, while time(NULL) gives different results each run.


2. C rand() Function Mechanics and Limitations

https://macronepal.com/c-rand-function-mechanics-and-limitations
Explains how rand() generates pseudo-random numbers between 0 and RAND_MAX, its deterministic nature, and limitations for security use cases.


3. C log() Function

https://macronepal.com/c-log-function-2
Covers natural logarithm calculation using <math.h> and its applications.


4. Mastering Date and Time in C

https://macronepal.com/mastering-date-and-time-in-c
Explains <time.h> functions like time(), clock(), difftime(), and struct tm.


5. Mastering time_t Type in C

https://macronepal.com/mastering-the-c-time_t-type-for-time-management
Explains time representation as seconds since Unix epoch and conversion functions.


6. C exp() Function

https://macronepal.com/c-exp-function-mechanics-and-implementation
Explains exponential function exp(x) and its scientific applications.


7. C log() Function (Alternate Guide)

https://macronepal.com/c-log-function
Comparison of log() and log10() with usage examples.


8. C log10() Function

https://macronepal.com/mastering-the-log10-function-in-c
Explains base-10 logarithm for engineering and scientific applications.


9. C tan() Function

https://macronepal.com/understanding-the-c-tan-function
Explains tangent function and radian-based calculations.


10. Random Numbers in C (Secure vs Predictable)

https://macronepal.com/mastering-c-random-numbers-for-secure-and-predictable-applications
Explains difference between rand() and secure randomness methods.


11. Free Online C Compiler

https://macronepal.com/free-online-c-code-compiler-2
Browser-based compiler for testing C programs instantly.


C Functions, Arguments, Parameters & Flow

Mastering Functions in C – Complete Guide

https://macronepal.com/c/mastering-functions-in-c-a-complete-guide/
Covers function structure, modular programming, and real-world usage.


Function Arguments in C

https://macronepal.com/c-function-arguments/
Explains how arguments are passed and used in function calls.


Function Parameters in C

https://macronepal.com/c-function-parameters/
Explains defining inputs for functions and matching them with arguments.


Function Declarations in C

https://macronepal.com/c-function-declarations-syntax-rules-and-best-practices/
Covers prototypes, syntax rules, and best practices.


Function Calls in C

https://macronepal.com/understanding-function-calls-in-c-syntax-mechanics-and-best-practices/
Explains execution flow and parameter handling during function calls.


Void Functions in C

https://macronepal.com/understanding-void-functions-in-c-syntax-patterns-and-best-practices/
Explains functions that do not return values.


Return Values in C

https://macronepal.com/c-return-values-mechanics-types-and-best-practices/
Explains different return types and how functions return results.


Pass-by-Value in C

https://macronepal.com/aws/understanding-pass-by-value-in-c-mechanics-implications-and-best-practices/
Explains how copies of variables are passed into functions.


Pass-by-Reference in C

https://macronepal.com/c/understanding-pass-by-reference-in-c-pointers-semantics-and-safe-practices/
Explains using pointers to modify original variables.


C strstr() Function

https://macronepal.com/aws/c-strstr-function/
Explains substring search inside strings in C.


C Preprocessor & Macros

https://macronepal.com/mastering-c-variadic-macros-for-flexible-debugging/
https://macronepal.com/mastering-the-stdc-macro-in-c/
https://macronepal.com/c-time-macro-mechanics-and-usage/
https://macronepal.com/understanding-the-c-date-macro/
https://macronepal.com/c-file-type/
https://macronepal.com/mastering-c-line-macro-for-debugging-and-diagnostics/
https://macronepal.com/mastering-predefined-macros-in-c/
https://macronepal.com/c-error-directive-mechanics-and-usage/
https://macronepal.com/understanding-the-c-pragma-directive/
https://macronepal.com/c-include-directive/


C Structures, Memory, Scope & Linkage

https://macronepal.com/mastering-structures-in-c/
https://macronepal.com/c-structure-declaration-mechanics-and-usage/
https://macronepal.com/c-structure-initialization-mechanics-and-best-practices/
https://macronepal.com/mastering-c-structure-member-access-for-reliable-data-handling/
https://macronepal.com/c-nested-structures/
https://macronepal.com/mastering-arrays-of-structures-in-c/
https://macronepal.com/c-structure-pointers-mechanics-and-implementation/
https://macronepal.com/understanding-c-structure-parameter-passing-mechanics/
https://macronepal.com/mastering-c-returning-structures-for-efficient-data-flow/
https://macronepal.com/c-self-referential-structures/
https://macronepal.com/mastering-structure-alignment-in-c/
https://macronepal.com/c-structure-padding-mechanics-and-optimization/
https://macronepal.com/understanding-c-flexible-array-members-mechanics-and-usage/
https://macronepal.com/mastering-c-anonymous-structures-for-flattened-data-layouts/
https://macronepal.com/c-unions/
https://macronepal.com/mastering-c-name-mangling-and-symbol-decoration/
https://macronepal.com/c-no-linkage-mechanics-and-scope-isolation/
https://macronepal.com/understanding-c-internal-linkage-mechanics-and-architecture/


C Scope, Storage Classes & Typedef

https://macronepal.com/mastering-function-prototype-scope-in-c/
https://macronepal.com/c-function-scope-mechanics-and-visibility/
https://macronepal.com/understanding-c-file-scope-mechanics-and-architecture/
https://macronepal.com/mastering-c-scope-rules-for-predictable-name-resolution/
https://macronepal.com/c-scope-rules/
https://macronepal.com/mastering-c-register-storage-class-for-historical-context-and-modern-alternatives/
https://macronepal.com/mastering-_thread_local-in-c/
https://macronepal.com/c-extern-storage-class-mechanics-and-usage/
https://macronepal.com/understanding-the-c-static-storage-class-mechanics-and-usage/
https://macronepal.com/c-auto-storage-class/
https://macronepal.com/c-typedef-with-pointers/


Extra Articles

https://macronepal.com/13757-2/
https://macronepal.com/13748-2/
https://macronepal.com/13747-2/
https://macronepal.com/13746-2/
https://macronepal.com/13745-2/
https://macronepal.com/13708-2/
https://macronepal.com/13707-2/
https://macronepal.com/13702-2/


Online Compilers

https://macronepal.com/free-html-online-code-compiler/
https://macronepal.com/free-online-python-code-compiler/
https://macronepal.com/free-online-python2-code-compiler/
https://macronepal.com/free-online-java-code-compiler/
https://macronepal.com/free-online-javascript-code-compiler/
https://macronepal.com/free-online-node-js-code-compiler/
https://macronepal.com/free-online-c-code-compiler/
https://macronepal.com/free-online-c-code-compiler-2/
https://macronepal.com/free-online-c-code-compiler-3/
https://macronepal.com/free-online-php-code-compiler/
https://macronepal.com/free-online-ruby-code-compiler/
https://macronepal.com/free-online-perl-code-compiler/
https://macronepal.com/free-online-lua-code-compiler/
https://macronepal.com/free-online-tcl-code-compiler/
https://macronepal.com/free-online-groovy-code-compiler/
https://macronepal.com/free-online-j-shell-code-compiler/
https://macronepal.com/free-online-haskell-code-compiler/
https://macronepal.com/free-online-scala-code-compiler/
https://macronepal.com/free-online-common-lisp-code-compiler/
https://macronepal.com/free-online-d-code-compiler/
https://macronepal.com/free-online-ada-code-compiler/
https://macronepal.com/free-erlang-code-compiler/
https://macronepal.com/free-online-assembly-code-compiler/

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper