google-site-verification: google61fe8ba583a51912.html
Mutex Usage in C: Complete Guide

Introduction to Mutex

A mutex (mutual exclusion) is a synchronization primitive used to protect shared resources from concurrent access in multithreaded programs. It ensures that only one thread can access a critical section at a time, preventing race conditions and data corruption.


Mutex Architecture Overview

Mutex Architecture ├── Mutex States │ ├── Unlocked - Available for locking │ ├── Locked - Owned by a thread │ └── Locked with waiters - Threads waiting ├── Mutex Types │ ├── Normal (fast) │ ├── Recursive │ ├── Error-checking │ └── Adaptive ├── Operations │ ├── lock() - Acquire mutex │ ├── unlock() - Release mutex │ ├── trylock() - Non-blocking attempt │ └── timedlock() - Lock with timeout └── Thread Synchronization ├── Critical Section ├── Condition Variables └── Read-Write Locks

Basic Mutex Operations

1. Initializing and Destroying Mutexes

#include <stdio.h> #include <pthread.h> #include <stdlib.h> #include <unistd.h> pthread_mutex_t mutex; int main() { // Initialize mutex with default attributes if (pthread_mutex_init(&mutex, NULL) != 0) { perror("mutex_init"); return 1; } printf("Mutex initialized successfully\n"); // Use mutex here // Destroy mutex when done pthread_mutex_destroy(&mutex); printf("Mutex destroyed\n"); return 0; }

2. Static Initialization

#include <stdio.h> #include <pthread.h> // Static initialization (simpler) pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; int main() { printf("Mutex statically initialized\n"); // No need to call pthread_mutex_init // Use mutex here // Still need to destroy pthread_mutex_destroy(&mutex); return 0; }

Basic Mutex Usage Examples

1. Protecting a Shared Counter

#include <stdio.h> #include <pthread.h> #include <unistd.h> #define NUM_THREADS 5 #define NUM_INCREMENTS 1000000 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; long long shared_counter = 0; void* increment_counter(void* arg) { int thread_id = *(int*)arg; for (int i = 0; i < NUM_INCREMENTS; i++) { // Lock mutex before accessing shared resource pthread_mutex_lock(&mutex); shared_counter++; pthread_mutex_unlock(&mutex); } printf("Thread %d finished\n", thread_id); return NULL; } int main() { pthread_t threads[NUM_THREADS]; int thread_ids[NUM_THREADS]; printf("Starting %d threads each incrementing %d times\n", NUM_THREADS, NUM_INCREMENTS); printf("Expected final count: %d\n", NUM_THREADS * NUM_INCREMENTS); // Create threads for (int i = 0; i < NUM_THREADS; i++) { thread_ids[i] = i; if (pthread_create(&threads[i], NULL, increment_counter, &thread_ids[i]) != 0) { perror("pthread_create"); return 1; } } // Wait for threads to complete for (int i = 0; i < NUM_THREADS; i++) { pthread_join(threads[i], NULL); } printf("Final counter value: %lld\n", shared_counter); printf("Expected value: %d\n", NUM_THREADS * NUM_INCREMENTS); pthread_mutex_destroy(&mutex); return 0; }

2. Without Mutex - Race Condition Demo

#include <stdio.h> #include <pthread.h> #include <unistd.h> #define NUM_THREADS 5 #define NUM_INCREMENTS 100000 long long shared_counter = 0; // No mutex protection void* unsafe_increment(void* arg) { int thread_id = *(int*)arg; for (int i = 0; i < NUM_INCREMENTS; i++) { shared_counter++; // RACE CONDITION! } printf("Thread %d finished\n", thread_id); return NULL; } int main() { pthread_t threads[NUM_THREADS]; int thread_ids[NUM_THREADS]; printf("=== RACE CONDITION DEMO ===\n"); printf("Running without mutex protection\n\n"); // Create threads for (int i = 0; i < NUM_THREADS; i++) { thread_ids[i] = i; pthread_create(&threads[i], NULL, unsafe_increment, &thread_ids[i]); } // Wait for threads for (int i = 0; i < NUM_THREADS; i++) { pthread_join(threads[i], NULL); } printf("\nExpected count: %d\n", NUM_THREADS * NUM_INCREMENTS); printf("Actual count: %lld\n", shared_counter); printf("Difference: %lld\n", (NUM_THREADS * NUM_INCREMENTS) - shared_counter); return 0; }

Mutex with trylock and timedlock

1. pthread_mutex_trylock() Example

#include <stdio.h> #include <pthread.h> #include <unistd.h> #include <errno.h> pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; void* trylock_worker(void* arg) { int thread_id = *(int*)arg; int attempts = 0; int max_attempts = 10; while (attempts < max_attempts) { // Try to lock without blocking int ret = pthread_mutex_trylock(&mutex); if (ret == 0) { // Successfully acquired lock printf("Thread %d acquired lock after %d attempts\n", thread_id, attempts); // Do some work sleep(1); pthread_mutex_unlock(&mutex); break; } else if (ret == EBUSY) { // Mutex is busy, try again printf("Thread %d: mutex busy, attempt %d\n", thread_id, attempts + 1); attempts++; sleep(1); // Wait before retrying } else { printf("Thread %d: trylock error: %d\n", thread_id, ret); break; } } if (attempts >= max_attempts) { printf("Thread %d giving up after %d attempts\n", thread_id, max_attempts); } return NULL; } int main() { pthread_t thread1, thread2; int id1 = 1, id2 = 2; printf("=== pthread_mutex_trylock() Demo ===\n"); pthread_create(&thread1, NULL, trylock_worker, &id1); sleep(1); // Give thread1 a head start pthread_create(&thread2, NULL, trylock_worker, &id2); pthread_join(thread1, NULL); pthread_join(thread2, NULL); pthread_mutex_destroy(&mutex); return 0; }

2. pthread_mutex_timedlock() Example

#include <stdio.h> #include <pthread.h> #include <unistd.h> #include <errno.h> #include <time.h> pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; void* timedlock_worker(void* arg) { int thread_id = *(int*)arg; struct timespec ts; // Calculate timeout time (current time + 3 seconds) clock_gettime(CLOCK_REALTIME, &ts); ts.tv_sec += 3; printf("Thread %d: trying to acquire lock with 3s timeout\n", thread_id); int ret = pthread_mutex_timedlock(&mutex, &ts); if (ret == 0) { printf("Thread %d: acquired lock\n", thread_id); sleep(2); // Hold lock for 2 seconds pthread_mutex_unlock(&mutex); printf("Thread %d: released lock\n", thread_id); } else if (ret == ETIMEDOUT) { printf("Thread %d: timed out waiting for lock\n", thread_id); } else { printf("Thread %d: error %d\n", thread_id, ret); } return NULL; } int main() { pthread_t thread1, thread2; int id1 = 1, id2 = 2; printf("=== pthread_mutex_timedlock() Demo ===\n"); pthread_create(&thread1, NULL, timedlock_worker, &id1); sleep(1); // Give thread1 time to acquire lock pthread_create(&thread2, NULL, timedlock_worker, &id2); pthread_join(thread1, NULL); pthread_join(thread2, NULL); pthread_mutex_destroy(&mutex); return 0; }

Mutex Attributes and Types

1. Setting Mutex Attributes

#include <stdio.h> #include <pthread.h> #include <errno.h> void demonstrate_mutex_attributes() { pthread_mutex_t mutex; pthread_mutexattr_t attr; // Initialize mutex attributes pthread_mutexattr_init(&attr); // Set mutex type pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK); // Set protocol pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT); // Set robustness (for robust mutexes) pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST); // Set process-shared attribute pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_PRIVATE); // Initialize mutex with attributes pthread_mutex_init(&mutex, &attr); // Get attribute values to verify int type; pthread_mutexattr_gettype(&attr, &type); printf("Mutex type: %d\n", type); int protocol; pthread_mutexattr_getprotocol(&attr, &protocol); printf("Protocol: %d\n", protocol); int robust; pthread_mutexattr_getrobust(&attr, &robust); printf("Robust: %d\n", robust); int pshared; pthread_mutexattr_getpshared(&attr, &pshared); printf("Process-shared: %d\n", pshared); // Cleanup pthread_mutex_destroy(&mutex); pthread_mutexattr_destroy(&attr); } int main() { demonstrate_mutex_attributes(); return 0; }

2. Different Mutex Types

#include <stdio.h> #include <pthread.h> #include <errno.h> // Normal mutex - deadlocks if same thread tries to lock twice void normal_mutex_demo() { pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; printf("\n=== Normal Mutex ===\n"); pthread_mutex_lock(&mutex); printf("First lock acquired\n"); // This would deadlock with normal mutex int ret = pthread_mutex_trylock(&mutex); if (ret == EBUSY) { printf("Second lock attempt would block (EBUSY)\n"); } pthread_mutex_unlock(&mutex); printf("Mutex unlocked\n"); } // Recursive mutex - same thread can lock multiple times void recursive_mutex_demo() { pthread_mutex_t mutex; pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&mutex, &attr); printf("\n=== Recursive Mutex ===\n"); pthread_mutex_lock(&mutex); printf("First lock acquired\n"); pthread_mutex_lock(&mutex); printf("Second lock acquired (recursive)\n"); printf("Lock count: 2\n"); pthread_mutex_unlock(&mutex); printf("One unlock, still locked\n"); pthread_mutex_unlock(&mutex); printf("Second unlock, now unlocked\n"); pthread_mutex_destroy(&mutex); pthread_mutexattr_destroy(&attr); } // Error-checking mutex - returns error on deadlock void errorcheck_mutex_demo() { pthread_mutex_t mutex; pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK); pthread_mutex_init(&mutex, &attr); printf("\n=== Error-Checking Mutex ===\n"); pthread_mutex_lock(&mutex); printf("First lock acquired\n"); int ret = pthread_mutex_lock(&mutex); if (ret == EDEADLK) { printf("Second lock attempt would deadlock (EDEADLK)\n"); } pthread_mutex_unlock(&mutex); printf("Mutex unlocked\n"); pthread_mutex_destroy(&mutex); pthread_mutexattr_destroy(&attr); } int main() { normal_mutex_demo(); recursive_mutex_demo(); errorcheck_mutex_demo(); return 0; }

Advanced Mutex Patterns

1. Guard Pattern (RAII-style in C)

#include <stdio.h> #include <pthread.h> #include <stdlib.h> typedef struct { pthread_mutex_t* mutex; } MutexGuard; // Automatically lock mutex on creation MutexGuard* guard_create(pthread_mutex_t* mutex) { MutexGuard* guard = malloc(sizeof(MutexGuard)); guard->mutex = mutex; pthread_mutex_lock(guard->mutex); printf("Mutex locked by guard\n"); return guard; } // Automatically unlock on destruction void guard_destroy(MutexGuard* guard) { pthread_mutex_unlock(guard->mutex); printf("Mutex unlocked by guard\n"); free(guard); } // Example usage void guarded_function(pthread_mutex_t* mutex) { MutexGuard* guard = guard_create(mutex); // Critical section - automatically protected printf("In critical section\n"); // No need to manually unlock - guard handles it guard_destroy(guard); } int main() { pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; printf("=== Guard Pattern Demo ===\n"); guarded_function(&mutex); pthread_mutex_destroy(&mutex); return 0; }

2. Double-Checked Locking Pattern

#include <stdio.h> #include <pthread.h> #include <stdlib.h> #include <unistd.h> typedef struct { int* data; int size; int initialized; pthread_mutex_t mutex; } SharedResource; SharedResource* resource = NULL; pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER; SharedResource* get_resource() { // First check (no locking) if (resource == NULL) { // Second check with locking pthread_mutex_lock(&init_mutex); if (resource == NULL) { printf("Initializing resource...\n"); resource = malloc(sizeof(SharedResource)); resource->size = 100; resource->data = malloc(resource->size * sizeof(int)); resource->initialized = 1; pthread_mutex_init(&resource->mutex, NULL); // Initialize data for (int i = 0; i < resource->size; i++) { resource->data[i] = i * i; } sleep(1); // Simulate initialization time printf("Resource initialized\n"); } pthread_mutex_unlock(&init_mutex); } return resource; } void* worker_thread(void* arg) { int thread_id = *(int*)arg; printf("Thread %d getting resource\n", thread_id); SharedResource* res = get_resource(); pthread_mutex_lock(&res->mutex); printf("Thread %d accessing resource\n", thread_id); printf(" data[50] = %d\n", res->data[50]); pthread_mutex_unlock(&res->mutex); return NULL; } int main() { pthread_t threads[5]; int thread_ids[5]; printf("=== Double-Checked Locking Demo ===\n"); for (int i = 0; i < 5; i++) { thread_ids[i] = i; pthread_create(&threads[i], NULL, worker_thread, &thread_ids[i]); } for (int i = 0; i < 5; i++) { pthread_join(threads[i], NULL); } // Cleanup if (resource) { pthread_mutex_destroy(&resource->mutex); free(resource->data); free(resource); } pthread_mutex_destroy(&init_mutex); return 0; }

3. Hierarchical Locking (Lock Ordering)

#include <stdio.h> #include <pthread.h> #include <unistd.h> typedef struct { pthread_mutex_t mutex; int level; char* name; } HierarchicalMutex; HierarchicalMutex mutex1 = {PTHREAD_MUTEX_INITIALIZER, 1, "Mutex1"}; HierarchicalMutex mutex2 = {PTHREAD_MUTEX_INITIALIZER, 2, "Mutex2"}; HierarchicalMutex mutex3 = {PTHREAD_MUTEX_INITIALIZER, 3, "Mutex3"}; // Thread-local current lock level __thread int current_level = 0; void hierarchical_lock(HierarchicalMutex* hm) { if (current_level > hm->level) { printf("ERROR: Trying to acquire %s (level %d) while holding higher level %d\n", hm->name, hm->level, current_level); return; } pthread_mutex_lock(&hm->mutex); current_level = hm->level; printf("Acquired %s (level %d), current level now %d\n", hm->name, hm->level, current_level); } void hierarchical_unlock(HierarchicalMutex* hm) { if (current_level != hm->level) { printf("ERROR: Trying to release %s but current level is %d\n", hm->name, current_level); return; } pthread_mutex_unlock(&hm->mutex); current_level = 0; // Reset for simplicity printf("Released %s (level %d)\n", hm->name, hm->level); } void* worker1(void* arg) { printf("\nWorker 1 - correct ordering\n"); hierarchical_lock(&mutex1); sleep(1); hierarchical_lock(&mutex2); sleep(1); hierarchical_lock(&mutex3); hierarchical_unlock(&mutex3); hierarchical_unlock(&mutex2); hierarchical_unlock(&mutex1); return NULL; } void* worker2(void* arg) { printf("\nWorker 2 - incorrect ordering (will deadlock)\n"); hierarchical_lock(&mutex3); sleep(1); hierarchical_lock(&mutex2); // This would deadlock if both run sleep(1); hierarchical_lock(&mutex1); hierarchical_unlock(&mutex1); hierarchical_unlock(&mutex2); hierarchical_unlock(&mutex3); return NULL; } int main() { pthread_t t1, t2; printf("=== Hierarchical Locking Demo ===\n"); printf("Lock levels: Mutex1=1, Mutex2=2, Mutex3=3\n"); printf("Always acquire locks in increasing level order\n\n"); // Run correct ordering pthread_create(&t1, NULL, worker1, NULL); pthread_join(t1, NULL); printf("\n--- Now try incorrect ordering ---\n"); // Run incorrect ordering (commented to avoid deadlock) // pthread_create(&t2, NULL, worker2, NULL); // pthread_join(t2, NULL); return 0; }

Robust Mutexes

1. Handling Thread Death While Holding Mutex

#include <stdio.h> #include <pthread.h> #include <unistd.h> #include <errno.h> pthread_mutex_t robust_mutex; void* thread1(void* arg) { pthread_mutex_lock(&robust_mutex); printf("Thread 1 acquired robust mutex\n"); // Simulate crash without unlocking printf("Thread 1 crashing while holding mutex...\n"); pthread_exit(NULL); // Thread exits without unlocking } void* thread2(void* arg) { sleep(1); // Wait for thread1 to crash printf("\nThread 2 attempting to lock robust mutex\n"); int ret = pthread_mutex_lock(&robust_mutex); if (ret == EOWNERDEAD) { printf("Thread 2: Previous owner died, recovering mutex\n"); // Mark mutex as consistent pthread_mutex_consistent(&robust_mutex); // Now we own the mutex printf("Thread 2: Mutex recovered and locked\n"); // Do work sleep(1); pthread_mutex_unlock(&robust_mutex); printf("Thread 2: Mutex unlocked\n"); } else if (ret == 0) { printf("Thread 2: Mutex locked normally\n"); pthread_mutex_unlock(&robust_mutex); } else { printf("Thread 2: Error %d\n", ret); } return NULL; } int main() { pthread_mutexattr_t attr; pthread_t t1, t2; // Initialize robust mutex pthread_mutexattr_init(&attr); pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST); pthread_mutex_init(&robust_mutex, &attr); printf("=== Robust Mutex Demo ===\n"); pthread_create(&t1, NULL, thread1, NULL); pthread_create(&t2, NULL, thread2, NULL); pthread_join(t1, NULL); pthread_join(t2, NULL); pthread_mutex_destroy(&robust_mutex); pthread_mutexattr_destroy(&attr); return 0; }

Performance Analysis

1. Mutex Performance Benchmark

#include <stdio.h> #include <pthread.h> #include <time.h> #include <unistd.h> #define NUM_THREADS 4 #define NUM_ITERATIONS 1000000 #define NUM_RUNS 5 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; long long counter = 0; void* benchmark_worker(void* arg) { for (int i = 0; i < NUM_ITERATIONS; i++) { pthread_mutex_lock(&mutex); counter++; pthread_mutex_unlock(&mutex); } return NULL; } double run_benchmark() { pthread_t threads[NUM_THREADS]; struct timespec start, end; counter = 0; clock_gettime(CLOCK_MONOTONIC, &start); for (int i = 0; i < NUM_THREADS; i++) { pthread_create(&threads[i], NULL, benchmark_worker, NULL); } for (int i = 0; i < NUM_THREADS; i++) { pthread_join(threads[i], NULL); } clock_gettime(CLOCK_MONOTONIC, &end); double elapsed = (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1e9; return elapsed; } int main() { printf("=== Mutex Performance Benchmark ===\n"); printf("Threads: %d, Iterations per thread: %d\n\n", NUM_THREADS, NUM_ITERATIONS); double total_time = 0; for (int run = 1; run <= NUM_RUNS; run++) { double time = run_benchmark(); total_time += time; printf("Run %d: %.3f seconds\n", run, time); } double avg_time = total_time / NUM_RUNS; long long total_ops = NUM_THREADS * NUM_ITERATIONS; double ops_per_sec = total_ops / avg_time; double ns_per_op = (avg_time * 1e9) / total_ops; printf("\n=== Results ===\n"); printf("Average time: %.3f seconds\n", avg_time); printf("Total operations: %lld\n", total_ops); printf("Operations/second: %.0f\n", ops_per_sec); printf("Nanoseconds per operation: %.1f\n", ns_per_op); pthread_mutex_destroy(&mutex); return 0; }

2. Mutex vs Atomic Operations

#include <stdio.h> #include <pthread.h> #include <stdatomic.h> #include <time.h> #define NUM_THREADS 4 #define NUM_ITERATIONS 1000000 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; long long mutex_counter = 0; atomic_long atomic_counter = 0; void* mutex_worker(void* arg) { for (int i = 0; i < NUM_ITERATIONS; i++) { pthread_mutex_lock(&mutex); mutex_counter++; pthread_mutex_unlock(&mutex); } return NULL; } void* atomic_worker(void* arg) { for (int i = 0; i < NUM_ITERATIONS; i++) { atomic_fetch_add(&atomic_counter, 1); } return NULL; } double benchmark_mutex() { pthread_t threads[NUM_THREADS]; struct timespec start, end; mutex_counter = 0; clock_gettime(CLOCK_MONOTONIC, &start); for (int i = 0; i < NUM_THREADS; i++) { pthread_create(&threads[i], NULL, mutex_worker, NULL); } for (int i = 0; i < NUM_THREADS; i++) { pthread_join(threads[i], NULL); } clock_gettime(CLOCK_MONOTONIC, &end); return (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1e9; } double benchmark_atomic() { pthread_t threads[NUM_THREADS]; struct timespec start, end; atomic_counter = 0; clock_gettime(CLOCK_MONOTONIC, &start); for (int i = 0; i < NUM_THREADS; i++) { pthread_create(&threads[i], NULL, atomic_worker, NULL); } for (int i = 0; i < NUM_THREADS; i++) { pthread_join(threads[i], NULL); } clock_gettime(CLOCK_MONOTONIC, &end); return (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1e9; } int main() { printf("=== Mutex vs Atomic Operations ===\n"); printf("Threads: %d, Iterations: %d\n\n", NUM_THREADS, NUM_ITERATIONS); double mutex_time = benchmark_mutex(); double atomic_time = benchmark_atomic(); printf("Mutex time: %.3f seconds\n", mutex_time); printf("Atomic time: %.3f seconds\n", atomic_time); printf("Atomic is %.2fx faster\n", mutex_time / atomic_time); pthread_mutex_destroy(&mutex); 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 EXAMPLE - DON'T DO THIS void* thread1_deadlock(void* arg) { printf("Thread 1: locking A\n"); pthread_mutex_lock(&mutex_a); sleep(1); // Give thread2 time to lock B 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); // Give thread1 time to lock A 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("(Commented out to avoid deadlock)\n\n"); // Uncomment to see deadlock /* pthread_create(&t1, NULL, thread1_deadlock, NULL); pthread_create(&t2, NULL, thread2_deadlock, NULL); pthread_join(t1, NULL); pthread_join(t2, NULL); */ printf("=== SAFE VERSION (same lock order) ===\n"); pthread_create(&t1, NULL, thread1_safe, NULL); pthread_create(&t2, NULL, thread2_safe, NULL); pthread_join(t1, NULL); pthread_join(t2, NULL); return 0; }

2. Lock Granularity

#include <stdio.h> #include <pthread.h> #include <unistd.h> #define ARRAY_SIZE 1000 #define NUM_THREADS 4 // Coarse-grained locking (single mutex for entire structure) typedef struct { int data[ARRAY_SIZE]; pthread_mutex_t mutex; } CoarseArray; // Fine-grained locking (mutex per element) typedef struct { int data[ARRAY_SIZE]; pthread_mutex_t mutexes[ARRAY_SIZE]; } FineArray; CoarseArray coarse; FineArray fine; // Initialize structures void init_structures() { pthread_mutex_init(&coarse.mutex, NULL); for (int i = 0; i < ARRAY_SIZE; i++) { pthread_mutex_init(&fine.mutexes[i], NULL); coarse.data[i] = i; fine.data[i] = i; } } // Coarse-grained worker void* coarse_worker(void* arg) { int id = *(int*)arg; for (int i = 0; i < 100; i++) { int idx = (id * 100 + i) % ARRAY_SIZE; pthread_mutex_lock(&coarse.mutex); coarse.data[idx]++; // Lock entire array pthread_mutex_unlock(&coarse.mutex); } return NULL; } // Fine-grained worker void* fine_worker(void* arg) { int id = *(int*)arg; for (int i = 0; i < 100; i++) { int idx = (id * 100 + i) % ARRAY_SIZE; pthread_mutex_lock(&fine.mutexes[idx]); // Lock only this element fine.data[idx]++; // Others can be accessed simultaneously pthread_mutex_unlock(&fine.mutexes[idx]); } return NULL; } double benchmark_coarse() { pthread_t threads[NUM_THREADS]; int ids[NUM_THREADS]; struct timespec start, end; clock_gettime(CLOCK_MONOTONIC, &start); for (int i = 0; i < NUM_THREADS; i++) { ids[i] = i; pthread_create(&threads[i], NULL, coarse_worker, &ids[i]); } for (int i = 0; i < NUM_THREADS; i++) { pthread_join(threads[i], NULL); } clock_gettime(CLOCK_MONOTONIC, &end); return (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1e9; } double benchmark_fine() { pthread_t threads[NUM_THREADS]; int ids[NUM_THREADS]; struct timespec start, end; clock_gettime(CLOCK_MONOTONIC, &start); for (int i = 0; i < NUM_THREADS; i++) { ids[i] = i; pthread_create(&threads[i], NULL, fine_worker, &ids[i]); } for (int i = 0; i < NUM_THREADS; i++) { pthread_join(threads[i], NULL); } clock_gettime(CLOCK_MONOTONIC, &end); return (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1e9; } int main() { init_structures(); printf("=== Lock Granularity Comparison ===\n"); printf("Array size: %d, Threads: %d\n\n", ARRAY_SIZE, NUM_THREADS); double coarse_time = benchmark_coarse(); double fine_time = benchmark_fine(); printf("Coarse-grained time: %.3f seconds\n", coarse_time); printf("Fine-grained time: %.3f seconds\n", fine_time); printf("Fine-grained is %.2fx faster\n", coarse_time / fine_time); pthread_mutex_destroy(&coarse.mutex); for (int i = 0; i < ARRAY_SIZE; i++) { pthread_mutex_destroy(&fine.mutexes[i]); } return 0; }

Best Practices Summary

Do's and Don'ts

// ✅ DO: Always lock before accessing shared data pthread_mutex_lock(&mutex); shared_counter++; pthread_mutex_unlock(&mutex); // ✅ DO: Use trylock for non-blocking attempts if (pthread_mutex_trylock(&mutex) == 0) { // Got lock pthread_mutex_unlock(&mutex); } // ✅ DO: Use consistent lock ordering to prevent deadlock // Always lock mutex A before mutex B // ✅ DO: Keep critical sections as small as possible pthread_mutex_lock(&mutex); // Only essential operations int temp = shared_data; pthread_mutex_unlock(&mutex); // Process temp outside lock // ✅ DO: Check return values of mutex functions int ret = pthread_mutex_lock(&mutex); if (ret != 0) { // Handle error } // ✅ DO: Use recursive mutexes when needed (but avoid if possible) // ❌ DON'T: Forget to unlock mutex pthread_mutex_lock(&mutex); // do something // Missing unlock! - will cause deadlock // ❌ DON'T: Lock mutex twice in same thread (unless recursive) pthread_mutex_lock(&mutex); pthread_mutex_lock(&mutex); // DEADLOCK! // ❌ DON'T: Access shared data without locking shared_data++; // RACE CONDITION! // ❌ DON'T: Hold locks while performing slow operations pthread_mutex_lock(&mutex); slow_operation(); // Blocks other threads unnecessarily pthread_mutex_unlock(&mutex); // ❌ DON'T: Destroy mutex while it's locked pthread_mutex_destroy(&mutex); // Undefined behavior if locked

Mutex Types Comparison

Mutex TypeLocking RulesUse CaseOverhead
NormalCan't relock in same threadGeneral purposeLowest
RecursiveCan relock in same threadRecursive functionsMedium
Error-checkingReturns error on misuseDebuggingHigher
AdaptiveSpins briefly then sleepsHigh contentionVariable
RobustHandles owner deathProcess robustnessHigher

Conclusion

Mutexes are fundamental to thread synchronization in C:

Key Concepts

  • Mutual exclusion - Only one thread can hold the lock
  • Critical sections - Code protected by mutex
  • Deadlock prevention - Consistent lock ordering
  • Lock granularity - Balance between contention and overhead
  • Robust mutexes - Handle thread termination

Best Practices

  1. Always pair lock/unlock operations
  2. Keep critical sections minimal
  3. Use consistent lock ordering
  4. Handle lock errors appropriately
  5. Consider lock-free alternatives for simple operations
  6. Test thoroughly for race conditions
  7. Profile to find contention points

When to Use Mutexes

  • Protecting shared data structures
  • Coordinating thread access to resources
  • Implementing thread-safe APIs
  • Managing shared state
  • Preventing race conditions

Mastering mutex usage is essential for writing correct and efficient multithreaded programs in C.

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