Chapter 10: Threads
Overview
This chapter introduces threads, a key concept in concurrent programming. Threads allow a process to perform multiple tasks simultaneously by sharing the same memory space. In Unix-based systems, threads are widely used for building responsive and efficient applications.
Key topics covered in this chapter include:
- What threads are and how they differ from processes
- Benefits and challenges of multithreading
- POSIX Threads (pthreads) API
- Creating, joining, and terminating threads
- Thread synchronization with mutexes and condition variables
- Common pitfalls and best practices
What Are Threads?
A thread is a lightweight unit of execution within a process. All threads in a process share the same code, data, and heap segments but have separate stacks and registers.
Threads vs. Processes
Feature | Thread | Process |
---|---|---|
Address Space | Shared | Separate |
Creation Overhead | Low | High |
Communication | Fast (shared memory) | Slow (IPC needed) |
Failure Isolation | No | Yes |
POSIX Threads (pthreads)
The POSIX standard provides a powerful API for thread programming in C. To use it, include the pthread.h
header and link with the -pthread
flag.
Creating Threads
#include <pthread.h>
#include <stdio.h>
void* say_hello(void* arg) {
printf("Hello from thread!\n");
return NULL;
}
int main() {
pthread_t tid;
pthread_create(&tid, NULL, say_hello, NULL);
pthread_join(tid, NULL);
return 0;
}
Thread Synchronization
When multiple threads share data, synchronization is necessary to avoid race conditions.
Mutex Example
#include <pthread.h>
#include <stdio.h>
int counter = 0;
pthread_mutex_t lock;
void* increment(void* arg) {
for (int i = 0; i < 10000; i++) {
pthread_mutex_lock(&lock);
counter++;
pthread_mutex_unlock(&lock);
}
return NULL;
}
int main() {
pthread_t t1, t2;
pthread_mutex_init(&lock, NULL);
pthread_create(&t1, NULL, increment, NULL);
pthread_create(&t2, NULL, increment, NULL);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
printf("Counter: %d\n", counter);
pthread_mutex_destroy(&lock);
return 0;
}
Condition Variables
Condition variables allow threads to wait for specific conditions to occur.
🧵 Interactive MicroSim
Explore how threads are created, scheduled, blocked, and synchronized inside an operating system — visualize ready queues, CPU scheduling, and mutex locking in action:
👉 Launch the Thread Scheduling and Synchronization Visualizer
Example: Producer-Consumer (Pseudo-code)
pthread_mutex_t mutex;
pthread_cond_t cond;
pthread_mutex_lock(&mutex);
while (!condition_met)
pthread_cond_wait(&cond, &mutex);
// Perform action
pthread_mutex_unlock(&mutex);
Thread Lifecycle
- Creation:
pthread_create()
starts a new thread. - Execution: Thread runs the specified function.
- Synchronization: Use
pthread_join()
to wait for a thread to finish. - Termination: Thread returns or calls
pthread_exit()
.
Common Pitfalls
- Race conditions: Unsynchronized access to shared data.
- Deadlocks: Threads wait on each other indefinitely.
- Resource leaks: Not joining or cleaning up threads properly.
- Excessive thread creation: Leads to overhead and performance issues.
Interactive Quiz
-
What is a key difference between threads and processes?
A. Threads have separate memory spaces
B. Threads share memory within a process
C. Threads are heavier than processesShow Answer
The correct answer is B. Threads share the same memory space, unlike processes.
-
Which function is used to create a new thread in POSIX?
A.
fork()
B.pthread_create()
C.exec()
Show Answer
The correct answer is B.
pthread_create()
creates a new thread. -
What is the purpose of a mutex?
A. To execute threads in parallel
B. To avoid context switching
C. To ensure exclusive access to shared resourcesShow Answer
The correct answer is C. Mutexes prevent race conditions by allowing only one thread to access a resource at a time.
Summary
- Threads enable concurrent execution within a process and share memory.
- Use
pthread_create()
,pthread_join()
, andpthread_exit()
to manage threads. - Synchronize threads with mutexes and condition variables to prevent race conditions.
- Proper design and cleanup are essential to avoid concurrency bugs.
In the next chapter, we’ll explore Process Scheduling and Synchronization, covering how the operating system manages threads and processes for efficient multitasking.
This chapter is based on material from "Advanced Programming in the UNIX Environment, Third Edition, by W. Richard Stevens and Stephen A. Rago, 2013."