Threads
Threads are light-weight processes within a process. They share with other threads their code section, data section and OS resources like open files and signals.
Thread Concept π‘
Thread and process are similar concepts in operating systems. But, they are different in the following ways:
Process:
- An instance of a program that is being executed.Β
- Need to make a separate system call
fork()
for each creation of a process to the OS. - Exists within its own address or memory space.
- Independent and treated as an isolated process by the OS.
Thread:
- A unit of execution (single sequence stream) within a process. It is a light-weight process.
- Shares with other threads their code section, data section and OS resources like open files and signals.
- Has its own program counter (PC), a register set, and a stack space.Β
- Be able to be scheduled by the operating system and run as independent entities within a process.
- A process can have multiple threads.
The table below shows the characteristics of both process and thread: π
Characteristic | Process | Thread |
---|---|---|
Definition | An instance of a running program. | An execution path within a process. |
Resources | Has its own separate memory space, file descriptors, etc. | Shares the resources (e.g., memory) of the parent process, but has its own stack, registers, etc. |
Isolation | Highly isolated; a crash in one process typically does not affect others. | Less isolated within the same process; a thread crash can cause the entire process to crash. |
Communication | Inter-Process Communication (IPC) is more complex, requiring special mechanisms (pipes, message queues, etc.). | Intra-process communication is simpler, as threads can directly access shared data. |
Creation Overhead | Creating a process is more expensive, as the OS needs to allocate new address spaces and other resources. | Creating a thread is less expensive, as it does not need to duplicate most of the parent processβs resources. |
Context Switching | Context switching costs are higher, involving saving and restoring more state information. | Context switching costs are lower, as only a smaller amount of state information needs to be saved and restored. |
Scheduling | Scheduled by the operating system scheduler. | Can be scheduled by either the operating system or user-level libraries. |
Multitasking | On a single-core CPU, processes appear to run concurrently over time. On multi-core CPUs, they can run in parallel. | More suitable for lightweight parallel or concurrent tasks within the same application. |
Usage | Suitable for running completely independent applications. | Suitable for task decomposition within the same application to improve responsiveness and resource utilization. |
Operation on Threads π
For coding with multiple threads, it is unlike Java, multithreading is not supported by C language standard. POSIX Threads
is a POSIX standard for threads and its implementation is provided by the pthread
library.
Thread Creation
To create a thread, we can use the following function:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
Parameters:
thread
: Pointer to an integer that will store the ID of the created thread.attr
: Thread attributes. It can beNULL
if not used. We can specifyPTHREAD_CREATE_JOINABLE
if we want to join the thread after it is created.start_routine
: The function that will be executed by the thread. It is a pointer to the function that will be executed by the thread.arg
: The argument that will be passed to the function.
Return value:
0
if the thread is created successfully. Otherwise, it returns an error code.EINVAL
if thestart_routine
isNULL
.EAGAIN
if the maximum number of threads has been reached.ENOMEM
if there is not enough memory to create the thread.EDEADLK
if the thread is already created.
Thread Termination
To terminate a thread, use the following function:
void pthread_exit (void *value_ptr);
Parameters:
value_ptr
: The value that will be returned by the thread when it is terminated. (NULL
if not used.)
Additional notes:
- This function is used to explicitly exit a thread. Typically, the
pthread_exit()
function is called after a thread has completed its work. - If
main()
finishes before the threads it has created, and exits withpthread_exit()
, the other threads will continue to execute. Otherwise, they will be automatically terminated whenmain()
finishes. - Use
pthread_exit()
to exit from all threads, especiallymain()
.
Thread Synchronization
Joining a thread is a blocking operation, which means that the thread will wait for the other thread to finish. We can use the following function:
int pthread_join(pthread_t thread, void *retval);
Parameters:
thread
: The ID of the thread that will be joined.retval
: The value that will be returned by the joined thread. (NULL
if not used.)
Return value:
0
if the thread is joined successfully. Otherwise, it returns an error code.- otherwise, it returns an error code.
Additional notes:
- It is impossible to join a detached thread.
- When a thread is created, one of its attributes defines whether it is joinable or detached. Detached means it can never be joined. (
PTHREAD_CREATE_DETACHED
orPTHREAD_CREATE_JOINABLE
)
Thread Mutex
Mutex (mutual exclusion) is a synchronization mechanism that allows only one thread to access a resource at a time. It is used to protect shared data from concurrent access. It is used to implement a lock mechanism:
Mutex should be declared as follows, where mutex
is the name of the mutex.
pthread_mutex_t mutex;
Mutex should be initialized before it is used:
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);
mutex
: Pointer to an integer that will store the mutex ID.mutexattr
: Mutex attributes. It can beNULL
to use the default.
Mutex should be destroyed after it is used:
int pthread_mutex_destroy(pthread_mutex_t *mutex);
mutex
: Pointer to an integer that stores the mutex ID.
To acquire a lock on the specified mutex variable:
int pthread_mutex_lock(pthread_mutex_t *mutex);
- If the mutex is successfully locked, it returns
0
. Otherwise, it returns an error code. - If the mutex is already locked by another thread, this will block until the mutex is unlocked.
To attempt to acquire a lock on the specified mutex variable:
int pthread_mutex_trylock(pthread_mutex_t *mutex);
- If the mutex is successfully locked, it returns
0
. Otherwise, it returns an error code. - If the mutex is already locked by another thread, it returns error.
- Useful in preventing deadlocks.
To release a lock on the specified mutex variable:
int pthread_mutex_unlock(pthread_mutex_t *mutex);
- If the mutex is successfully unlocked, it returns
0
. Otherwise, it returns an error code. For example:- The Mutex was already unlocked.
- The Mutex was owned by another thread.
Related Posts π
π Operating System Overview
π Operating System Security
π Processes
π File System
π Memory Management
Comments