pthreads tutorial explains pthreads (POSIX threads) in simple, beginner-friendly language. You’ll learn what threads are, why they’re useful, and how to create them using the pthread library in C. The article covers basic thread creation, joining, passing data between threads, and avoiding common issues like race conditions. It also includes example programs and common interview questions to help you feel confident using pthreads in real-world coding or interviews.
What Are Threads?
Imagine your computer running several tasks at once — like downloading a file, updating the screen, and playing music. Threads are the “mini-programs” inside a single process that let you do multiple tasks at the same time.
- A process has its own memory space.
- A thread shares memory space with other threads in the same process.
Threads help programs:
✅ run faster on multi-core CPUs
✅ stay responsive (e.g. in apps with user interfaces)
✅ manage parallel tasks (e.g. computations, I/O)
What is pthread?
pthreads (POSIX threads) is a library in C/C++ on Unix-like systems (Linux, macOS, etc.) that helps you create and manage threads.
- Defined in pthread.h
- Functions start with
pthread_
How to Create a Thread
Here’s the simplest pthread program:
#include <stdio.h>
#include <pthread.h>
void* myThreadFunction(void* arg) {
printf("Hello from the new thread!\n");
return NULL;
}
int main() {
pthread_t threadId;
// Create a new thread
pthread_create(&threadId, NULL, myThreadFunction, NULL);
// Wait for the thread to finish
pthread_join(threadId, NULL);
printf("Back in main thread.\n");
return 0;
}
How this works:
✅ pthread_create(...)
creates a thread and runs myThreadFunction
in it.
✅ pthread_join(...)
waits until the thread finishes.
pthread_create – Function Details
int pthread_create(
pthread_t *thread, // Thread ID output
const pthread_attr_t *attr, // Attributes (can be NULL)
void *(*start_routine)(void *), // Function the thread runs
void *arg // Argument passed to that function
);
Example:
pthread_create(&threadId, NULL, myThreadFunction, NULL);
pthread_join
Once a thread starts, the main thread might finish too early. We don’t want the program to exit while other threads are still running. So, we join the thread:
pthread_join(threadId, NULL);
This waits until the thread is done.
Passing Data to Threads
Threads often need data. You can pass arguments via the void*
argument:
#include <stdio.h>
#include <pthread.h>
void* printNumber(void* arg) {
int num = *(int*)arg;
printf("Thread received number: %d\n", num);
return NULL;
}
int main() {
pthread_t tid;
int val = 42;
pthread_create(&tid, NULL, printNumber, &val);
pthread_join(tid, NULL);
return 0;
}
Threads Share Memory!
Unlike processes, threads share global variables and heap memory.
⚠️ Danger: Two threads writing to the same variable at the same time = Race Condition!
Synchronization with Mutexes
Mutex = Mutual Exclusion. It prevents simultaneous access.
Example:
#include <stdio.h>
#include <pthread.h>
pthread_mutex_t lock;
void* myThread(void* arg) {
pthread_mutex_lock(&lock);
// Critical section
printf("Thread in critical section\n");
pthread_mutex_unlock(&lock);
return NULL;
}
int main() {
pthread_t tid;
pthread_mutex_init(&lock, NULL);
pthread_create(&tid, NULL, myThread, NULL);
pthread_join(tid, NULL);
pthread_mutex_destroy(&lock);
return 0;
}
Other Useful pthread Functions
Function | Description |
---|---|
pthread_exit() | End a thread voluntarily |
pthread_self() | Get the current thread ID |
pthread_mutex_init() | Initialize a mutex |
pthread_mutex_lock() | Lock a mutex |
pthread_mutex_unlock() | Unlock a mutex |
pthread_mutex_destroy() | Free mutex resources |
Advantages of pthreads
✅ Speed — threads can run concurrently.
✅ Shared memory — no need for complex communication like pipes or sockets.
✅ Lightweight — creating threads costs less than creating processes.
pthread Interview Questions
Here are some beginner-to-intermediate interview questions on pthreads:
Q1. What is the difference between a process and a thread?
- Process: Separate memory space, separate code, own resources.
- Thread: Shares memory and resources with other threads in same process.
Q2. What is pthread?
A POSIX standard library in C/C++ for creating and managing threads on Unix-like systems.
Q3. How do you create a thread using pthread?
Using pthread_create
:
pthread_create(&tid, NULL, myThreadFunction, NULL);
Q4. How can you wait for a thread to finish?
By calling:
pthread_join(threadId, NULL);
Q5. Why do we need synchronization primitives like mutexes?
Because threads share memory. To avoid race conditions when two threads access or modify the same data]\\][]\
Q6. What is a race condition?
When two or more threads read or write shared data at the same time, leading to unpredictable results.
Q7. What’s the purpose of pthread_mutex_lock and pthread_mutex_unlock?
They lock/unlock a mutex so that only one thread can enter a critical section at a time.
Q8. Can you pass arguments to threads in pthread?
Yes! Use the void* arg
parameter in pthread_create
to pass data.
Q9. What happens if you don’t call pthread_join?
The main thread might exit before other threads finish. This may terminate the whole process before threads complete their work.
Q10. What is a deadlock?
When two or more threads each hold a lock and wait forever for each other to release it.
How to Avoid Race Conditions
What is a Race Condition?
A race condition happens when:
- Two or more threads access the same variable or data at the same time
- At least one of them writes to it
Result: The outcome depends on who “wins the race” — i.e. which thread runs first. This causes bugs that happen randomly.
Example of a race condition:
#include <stdio.h>
#include <pthread.h>
int counter = 0;
void* increment(void* arg) {
for (int i = 0; i < 100000; i++) {
counter++;
}
return NULL;
}
int main() {
pthread_t t1, t2;
pthread_create(&t1, NULL, increment, NULL);
pthread_create(&t2, NULL, increment, NULL);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
printf("Final counter value: %d\n", counter);
return 0;
}
Expected output: 200000
Actual output: Sometimes less than 200000!
How to Avoid Race Conditions
Here’s how you can avoid race conditions in pthreads:
✅ 1. Use Mutexes
A mutex lets only one thread enter a critical section at a time.
Example:
#include <stdio.h>
#include <pthread.h>
int counter = 0;
pthread_mutex_t lock;
void* increment(void* arg) {
for (int i = 0; i < 100000; 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);
pthread_mutex_destroy(&lock);
printf("Final counter value: %d\n", counter);
return 0;
}
Now, output is reliably 200000.
✅ 2. Use Read-Write Locks
If many threads read and only some write, use read-write locks (pthread_rwlock).
- Multiple readers allowed.
- Writers get exclusive access.
✅ 3. Use Atomic Operations
For simple variables, you can use atomic operations instead of mutexes.
Example (using GCC atomic built-ins):
__sync_fetch_and_add(&counter, 1);
These are lock-free and faster.
✅ 4. Minimize Shared Data
- Avoid sharing data between threads if you can.
- Give each thread its own variables.
- Less sharing → fewer race conditions!
✅ 5. Proper Thread Join / Synchronization
Always join threads before accessing shared results.
pthread_join(t1, NULL);
pthread_join(t2, NULL);
Otherwise, main might read data while threads are still changing it.
✅ 6. Condition Variables (Advanced)
When threads wait for some condition to become true, combine mutexes with condition variables for safe signaling.
Simple Rule
“Protect all shared data with synchronization.”
Any time multiple threads:
- Read and write shared variables
- Modify global variables
- Update shared structures
→ Use mutexes or other mechanisms.
How To Spot Race Conditions?
They often show up as:
- Random crashes
- Wrong calculation results
- Bugs that “sometimes” happen
FAQ – Beginner-Friendly Guide: Understanding pthreads
Q1. What are pthreads?
Answer:
pthreads stands for POSIX threads. It’s a library in C/C++ used to create and manage threads on Unix-like systems like Linux and macOS.
Q2. Why should I use threads?
Answer:
Threads help your program do multiple things at once, like:
- Handling many tasks faster
- Keeping apps responsive
- Using multi-core CPUs efficiently
Q3. What’s the difference between a process and a thread?
Answer:
- A process has its own memory space.
- A thread shares memory with other threads in the same process.
Threads are lighter and faster to create than processes.
Q4. How do I create a thread in pthread?
Answer:
Use pthread_create
. Example:
pthread_create(&threadId, NULL, myFunction, NULL);
Q5. What is pthread_join?
Answer:pthread_join
waits for a thread to finish before moving on. It prevents your program from ending too soon while threads are still working.
Q6. Can I pass data to threads?
Answer:
Yes! You can pass a pointer as an argument to your thread function.
Example:
pthread_create(&tid, NULL, myFunction, &myData);
Q7. What is a race condition?
Answer:
A race condition happens when two threads change the same data at the same time, causing unpredictable results.
Q8. How do I avoid race conditions in pthreads?
Answer:
Use a mutex to lock shared data so only one thread can change it at a time.
Q9. Do threads share variables?
Answer:
Yes. Threads share:
- Global variables
- Heap memory
Each thread has its own stack for local variables.
Q10. Is pthread available on Windows?
Answer:
pthreads is mainly for Unix-like systems. On Windows, you might use:
- Windows Threads API
- Libraries like pthreads-win32
- C++11’s
<thread>
instead
Tutorials & Guides
- Multithreaded Programming (POSIX pthreads Tutorial) – randu.org
A comprehensive guide covering thread synchronization, mutexes, and practical examples to get you started with pthreads. - Multi-Threaded Programming With POSIX Threads – Villanova University
A well-structured tutorial introducing POSIX threads, including examples and explanations suitable for beginners. - POSIX Threads Programming – LLNL HPC Tutorials
A detailed guide from Lawrence Livermore National Laboratory, covering thread creation, synchronization, and more.
🎥 Video Tutorials
- Introduction To Threads (pthreads) | C Programming Tutorial
A beginner-friendly video tutorial that introduces pthreads in C, explaining the basics of thread creation and management.
📚 Reference Materials
- POSIX Threads – Wikipedia
A concise overview of POSIX threads, their history, and how they fit into modern programming. - POSIX Threads in OS – Tutorialspoint
An accessible explanation of POSIX threads and their role in operating systems, ideal for newcomers.
Mr. Raj Kumar is a highly experienced Technical Content Engineer with 7 years of dedicated expertise in the intricate field of embedded systems. At Embedded Prep, Raj is at the forefront of creating and curating high-quality technical content designed to educate and empower aspiring and seasoned professionals in the embedded domain.
Throughout his career, Raj has honed a unique skill set that bridges the gap between deep technical understanding and effective communication. His work encompasses a wide range of educational materials, including in-depth tutorials, practical guides, course modules, and insightful articles focused on embedded hardware and software solutions. He possesses a strong grasp of embedded architectures, microcontrollers, real-time operating systems (RTOS), firmware development, and various communication protocols relevant to the embedded industry.
Raj is adept at collaborating closely with subject matter experts, engineers, and instructional designers to ensure the accuracy, completeness, and pedagogical effectiveness of the content. His meticulous attention to detail and commitment to clarity are instrumental in transforming complex embedded concepts into easily digestible and engaging learning experiences. At Embedded Prep, he plays a crucial role in building a robust knowledge base that helps learners master the complexities of embedded technologies.
Leave a Reply