Fundamentals 20 min read

Mastering Pthreads: Complete Guide to Thread Creation, Sync, and Management on Linux

This article explains why traditional Unix fork/exec models are costly, introduces POSIX threads (Pthreads) as a lightweight alternative, and provides detailed guidance on thread data structures, creation, termination, synchronization primitives, and attribute configuration with practical code examples for Linux developers.

Liangxu Linux
Liangxu Linux
Liangxu Linux
Mastering Pthreads: Complete Guide to Thread Creation, Sync, and Management on Linux

Why Threads Over Fork?

In the classic Unix model a process forks a child to handle a task, which works well but has two major drawbacks: fork is expensive because it copies the parent’s memory image, and inter‑process communication (IPC) is needed to exchange data after the child finishes. Threads, often called lightweight processes, are 10–100 times faster to create and share the same address space, simplifying data sharing while introducing synchronization challenges.

1. Thread Basics

Data structures : pthread_t (thread ID) and pthread_attr_t (attributes).

Core functions : pthread_create(), pthread_exit(), pthread_cancel(), pthread_join(), pthread_attr_init(), pthread_attr_setdetachstate(), pthread_attr_getdetachstate(), pthread_attr_destroy(), pthread_kill().

Synchronization primitives : mutexes, condition variables, reader‑writer locks, thread‑specific data functions.

2. Thread Components

A thread shares the process’s global memory, open files, signal handlers, working directory, UID/GID, etc., but has its own ID, register set (including program counter and stack pointer), stack, error number, signal mask, priority, and thread‑specific data.

3. Defining Threads

#include <pthread.h>
int pthread_equal(pthread_t tid1, pthread_t tid2);
#include <pthread.h>
 pthread_t pthread_self(void);

4. Creating Threads

#include <pthread.h>
int pthread_create(pthread_t *restrict tidp,
                  const pthread_attr_t *restrict attr,
                  void *(*start_rtn)(void *),
                  void *restrict arg);

Parameters: tidp: receives the new thread’s ID. attr: thread attributes (use NULL for defaults). start_rtn: pointer to the function the thread will run; it must accept and return void *. arg: argument passed to start_rtn.

If creation succeeds, the thread ID is stored in tidp. Attributes such as stack size, detach state, and scheduling priority can be set via a pthread_attr_t object before the call.

5. Exiting Threads

exit

, _Exit, _exit terminate the whole process.

Thread termination methods:

Return from the thread function.

Another thread calls pthread_cancel().

The thread calls pthread_exit(). pthread_exit(void *rval_ptr) ends the calling thread and makes rval_ptr the thread’s return value. pthread_join(pthread_t thread, void **rval_ptr) blocks until thread finishes and retrieves its return value.

#include <pthread.h>
void pthread_exit(void *rval_ptr);
int pthread_join(pthread_t thread, void **rval_ptr);

When a thread is not detached, its exit status is retained until another thread joins it. Detached threads release resources automatically upon termination.

6. Thread Synchronization

Mutexes

Used to protect critical sections so that only one thread executes them at a time.

#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
                       const pthread_mutexattr_t *restrict attr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);

Example of a producer‑consumer pair using a mutex:

pthread_mutex_t mutex;
int buffer_has_item = 0;
char buffer;

void writer_function(void) {
    while (1) {
        pthread_mutex_lock(&mutex);
        if (!buffer_has_item) {
            buffer = make_new_item();
            buffer_has_item = 1;
        }
        pthread_mutex_unlock(&mutex);
        pthread_delay_np(&delay);
    }
}

void reader_function(void) {
    while (1) {
        pthread_mutex_lock(&mutex);
        if (buffer_has_item) {
            consume_item(buffer);
            buffer_has_item = 0;
        }
        pthread_mutex_unlock(&mutex);
        pthread_delay_np(&delay);
    }
}

Deadlock can occur if two threads lock resources in opposite order; using pthread_mutex_trylock helps detect such situations.

Reader‑Writer Locks

#include <pthread.h>
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
                        const pthread_rwlockattr_t *restrict attr);
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);

Multiple threads may hold a read lock simultaneously, but only one may hold a write lock.

Condition Variables

#include <pthread.h>
int pthread_cond_init(pthread_cond_t *restrict cond,
                     const pthread_condattr_t *restrict attr);
int pthread_cond_destroy(pthread_cond_t *cond);
int pthread_cond_wait(pthread_cond_t *restrict cond,
                     pthread_mutex_t *restrict mutex);
int pthread_cond_timedwait(pthread_cond_t *restrict cond,
                           pthread_mutex_t *restrict mutex,
                           const struct timespec *restrict timeout);
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);

Typical pattern:

pthread_mutex_t count_lock;
pthread_cond_t count_nonzero;
unsigned count;

void decrement_count(void) {
    pthread_mutex_lock(&count_lock);
    while (count == 0)
        pthread_cond_wait(&count_nonzero, &count_lock);
    --count;
    pthread_mutex_unlock(&count_lock);
}

void increment_count(void) {
    pthread_mutex_lock(&count_lock);
    if (count == 0)
        pthread_cond_signal(&count_nonzero);
    ++count;
    pthread_mutex_unlock(&count_lock);
}

The timespec structure used for timed waits is defined as:

struct timespec {
    time_t tv_sec;   /* seconds */
    long   tv_nsec;  /* nanoseconds */
};

7. Thread Attributes

Attributes are stored in a pthread_attr_t object and must be initialized with pthread_attr_init(). Important attributes include:

Scope (binding) : Determines whether a thread is bound to a specific lightweight process (LWP). Use pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM) for bound threads.

Detach state : Controls whether a thread can be joined. Set with

pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)

or PTHREAD_CREATE_JOINABLE.

Priority : Stored in a struct sched_param. Retrieve and set with pthread_attr_getschedparam() and pthread_attr_setschedparam().

#include <pthread.h>
#include <sched.h>

pthread_attr_t attr;
pthread_t tid;
struct sched_param param;
int newprio = 20;

pthread_attr_init(&attr);
pthread_attr_getschedparam(&attr, &param);
param.sched_priority = newprio;
pthread_attr_setschedparam(&attr, &param);
pthread_create(&tid, &attr, (void *)myfunction, NULL);
Reference: https://www.cnblogs.com/Stultz-Lee/p/6702922.html
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

concurrencySynchronizationmultithreadingthreadingpthreads
Liangxu Linux
Written by

Liangxu Linux

Liangxu, a self‑taught IT professional now working as a Linux development engineer at a Fortune 500 multinational, shares extensive Linux knowledge—fundamentals, applications, tools, plus Git, databases, Raspberry Pi, etc. (Reply “Linux” to receive essential resources.)

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.