The POSIX thread (pthread) is based on the POSIX Threads standard, which defines a set of functions and data types for creating and controlling threads in a portable and consistent manner. PThread is a widely-used implementation of this standard, and is commonly used in multi-threaded programming on Linux and other Unix-like operating systems.
PThread level of control can be useful in some cases, but it can also make the programming of concurrent applications more complex and error-prone.
A thread is a unit of execution within a process that can be scheduled and executed by the operating system. In the Shared Memory Model, threads have their own local resources, such as stack and register state, but they also have access to the shared resources of the process in which they run. This allows multiple threads to cooperate since they can communicate with each other implicitly by reading and writing to shared system memory locations. However, this can lead to sync issues and the programmer must use mechanisms to ensure that threads access shared resources in a safe and orderly manner.
Threads created in a pthread application are not hierarchically organized. In the pthread library, threads are considered to be independent entities that can be scheduled and executed by the operating system in an arbitrary order.
int pthread_create(pthread_t * thread, const pthread_attr_t * attr, void *(*start_routine)(void *), void * arg)
-
thread
, is a pointer to apthread_t
variable that will be set to the ID of the newly created thread. -
attr
, is a pointer to apthread_attr_t
structure that specifies optional attributes for the new thread. This parameter can be set toNULL
to use the default values for all attributes. -
start_routine
, is a pointer to a function that will be executed by the new thread when it is created. This function must have a return type ofvoid *
and take a singlevoid *
argument. -
arg
, is a pointer to data that will be passed as an argument to thestart_routine
function when the new thread is created.
struct thread_args {
int arg1;
char *arg2;
};
void * my_thread_func(void *args) {
//remember the cast
struct thread_args * my_args = (struct thread_args *) args;
int arg1 = my_args->arg1;
char *arg2 = my_args->arg2;
// Use the arguments in the thread function...
}
int main() {
pthread_t my_thread;
struct thread_args my_args;
my_args.arg1 = 10;
my_args.arg2 = "hello";
pthread_create(&my_thread, NULL, my_thread_func, &my_args);
// Other code...
}
Note that an important step in pthreads to communicate variables is how void *args
works: in this example the struct thread_args
is defined to contain the two arguments that will be passed to the thread function.
The generalization of this approach is to pass arguments of any type to a thread function as long as you define a struct that can hold the necessary data and pass a pointer to this struct to pthread_create
.
Pthread_join is a function used in the POSIX thread (pthread) library to synchronize the execution of threads. It allows one thread to wait for the completion of another thread. When a thread calls pthread_join, it is suspended until the target thread (the thread being joined) completes its execution. The pthread_join function returns the exit status of the target thread, which can be used to determine whether it terminated successfully or not.
#include <pthread.h>
#include <stdio.h>
void *my_thread_func(void *args) {
// Do some work in the thread...
return NULL;
}
int main() {
pthread_t my_thread;
pthread_create(&my_thread, NULL, my_thread_func, NULL);
// Do some work in the main thread...
pthread_join(my_thread, NULL);
// Continue execution after the thread has completed...
return 0;
}
pthread_barrier_init(pthread_barrier_t barrier, pthread_barrierattr_t * attr, unsigned int count);
count
is the number of variable to wait.
attr
is the number of variable to wait.
#include <pthread.h>
#include <stdio.h>
pthread_barrier_t my_barrier;
void *my_thread_func(void *args) {
// Do some work in the thread...
pthread_barrier_wait(&my_barrier);
// Continue execution after the barrier is reached...
return NULL;
}
int main() {
pthread_barrier_init(&my_barrier, NULL, 2);
pthread_t my_thread;
pthread_create(&my_thread, NULL, my_thread_func, NULL);
// Do some work in the main thread...
pthread_barrier_wait(&my_barrier);
// Continue execution after the barrier is reached...
pthread_join(my_thread, NULL);
return 0;
}
Both the main thread and the new thread do some work, and then each calls the pthread_barrier_wait(&barrier)
function to wait for the other thread to reach the barrier. When both threads have reached the barrier, they are released and can continue execution.
A mutex variable is a special type of lock that allows only one thread to hold it at any given time. When a thread acquires a mutex, it prevents other threads from acquiring the same mutex, ensuring that only one thread can access the protected resource at a time.
The pthread_cond_wait
function release the mutex that is associated with the condition variable. This allows other threads to acquire the mutex and access the shared resource or data structure that is protected by the mutex.
#include <pthread.h>
#include <stdio.h>
pthread_mutex_t my_mutex;
pthread_cond_t my_cond;
int my_shared_var = 0;
void *my_thread_func(void *args) {
pthread_mutex_lock(&my_mutex);
// Wait for the condition to be signaled
while (my_shared_var == 0) {
pthread_cond_wait(&my_cond, &my_mutex);
}
// Do some work with the shared variable...
pthread_mutex_unlock(&my_mutex);
return NULL;
}
int main() {
pthread_mutex_init(&my_mutex, NULL);
pthread_cond_init(&my_cond, NULL);
pthread_t my_thread;
pthread_create(&my_thread, NULL, my_thread_func, NULL);
// Do some work in the main thread...
pthread_mutex_lock(&my_mutex);
my_shared_var = 1;
pthread_cond_signal(&my_cond);
pthread_mutex_unlock(&my_mutex);
pthread_join(my_thread, NULL);
return 0;
}
pthread_cond_signal(&cond)
signals only 1 thread!
pthread_cond_broadcast(&cond)
signals all the threads that are "listening" that condition variable.