APUE Chapter XI notes

Posted by globetrottingmike on Wed, 19 Jan 2022 19:36:27 +0100

Chapter 11 thread

11.3 thread identification

  • Each thread has a thread ID. The thread ID is meaningful only in the context of the process to which it belongs.
  • When implemented, you can use - a structure to represent pthread_ Tdata type.
  • A function that compares two thread ID s
#include <pthread.h>
int pthread_equal(pthread_t tidl, pthread_t tid2);
//Return value: If equal, return a non-zero value; Otherwise, return 0
  • Function to get its own thread ID
#include <pthread.h>
pthread_t pthread_self(void) ;
//Return value: thread D of calling thread

11.4 thread creation

  • Threads can call pthread_ The create function creates.
#include <pthread.h>
int pthread_create(pthread_t *restrict tidp,
    const pthread_attr_t *restrict attr ,
    void * (*start_rn)(void *) , void *restrict arg);
//Return value: 0 if successful; Otherwise, return to the wrong number
  • The thread ID of the newly created thread will be set to the memory unit pointed to by tidp. The attr parameter is used to customize various thread properties. The newly created thread starts from start_ The RTN function starts running at the address of, which has only one typeless pointer parameter arg.
  • The newly created thread can access the address space of the process and inherit the floating-point environment and signal mask word of the calling thread, but the thread's pending signal set is cleared.
  • Each thread provides a copy of errno.

11.5 thread termination

  • If any thread in the process calls exit_ Exit or_ Exit, then the whole process will terminate.
  • Thread exit mode:
  1. The thread can simply return from the startup routine, and the return value is the exit code of the thread.
  2. A thread can be canceled by another thread in the same process.
  3. Thread calls pthread_exit.
#include <pthread.h>
void pthread_exit(void *rval_ptr);
  • rval_ The PTR parameter is a typeless pointer, similar to a single parameter passed to the startup routine. Other threads in the process can also call pthread_ The join function accesses this pointer.
#include <pthread.h>
int pthread_join(pthread_t thread, void **rval_ptr);
//Return value: 0 if successful; Otherwise, the error number is returned
  • The calling thread will block until the specified thread calls pthread exit, returns from the startup routine, or is cancelled.
    If a thread simply returns from its startup routine, rvalptr contains the return code. If the thread is canceled, the memory unit specified by rvalptr is set to PTHREAD_CANCELED.
  • You can call pthread_join automatically puts threads in a detached state so that resources can be recovered. If the thread is already in a detached state, pthread_ The join call will fail and return EINVAI.
  • If you are not interested in the return value of the thread, you can set rvalptr to NULL.
  • Threads can call pthread_cancel function to request the cancellation of other threads in the same process.
#include <pthread.h>
int pthread_cancel(pthread_t tid);
//Return value: 0 if successful; Otherwise, the error number is returned
  • By default, pthread_ The cancel function causes the thread identified by nid to behave as if pthread was called_ Pthread of canceled_ Exit function. However, it only makes a request.
  • A thread can schedule the functions it needs to call when it exits
#include <pthread.h>
void pthread_cleanup_push(void(*rtm)(void*), void* ang);
void pthread_cleanup_pop(int execute);
  • Clean up function work when the thread performs the following actions
  1. Call pthread_exit
  2. When responding to a cancel request
  3. Pthread is called with a non-zero execute parameter_ cleanup_ Pop time
  • If the execute parameter is set to 0, the cleanup function will not be called.
  • pthread_cleanup_pop will delete the last pthread_ cleanup_ The cleanup handler established by the push call.
  • Pthread can be called_ Detach detach thread.
#include <pthread.h>
int pthread_detach(pthread_t tid);
//Return value: 0 if successful; Otherwise, the error number is returned

11.6 thread synchronization

  • When a thread can modify variables and other threads can read or modify them, we need to synchronize these threads
  • The way programs use variables can also cause competition and lead to inconsistencies.

11.6.1 mutual exclusion

  • Mutually exclusive variables are pthread_mutex_t is represented by the data type.
  • Before using a mutex variable, it must first be initialized and can be set to the constant PTHREADMUTEX_INITIALIZER (only applicable to statically allocated mutexes), or you can call pthread_mutex_init function to initialize. If the mutex is allocated dynamically, pthread needs to be called before releasing memory_ mutex_ destroy.
#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);
//Return values of two functions: 0 if successful; Otherwise, the error number is returned
  • To initialize the mutex with the default attribute, simply set attr to NULL.
  • To lock the mutex, you need to call pthread_mutex_lock. If the mutex is locked, the calling thread will block until the mutex is unlocked.
  • Pthread needs to be called to unlock the mutex_ mutex_ unlock.
  • If pthread mutex is called_ When the mutex is unlocked during trylock, pthread mutex_trylock will lock the mutex without blocking and return 0 directly, otherwise pthread mutex_trylock will fail, cannot lock the mutex, and returns EBUSY.
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t*mutex);
int pthread_mutex_trylock(pthread mutex_t*mutex);
int pthread_mutex_unlock(pthread_mutex_t*muter);
//Return values of all functions: 0 if successful; Otherwise, the error number is returned

11.6.2 deadlock avoidance

  • When two mutexes are needed at the same time, they are always locked in the same order, which can avoid deadlock.

11.6.3 pthread function_ mutex_ timedlock

  • pthread_ mutex_ The timedlock mutex primitive allows binding thread blocking times. When the timeout value is reached, pthread_mutex_timedlock does not lock the mutex, but returns the error code ETIMEDOUT
#include <pthread.h>
#include <time.h>
int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex, const struct timespec *restrict tsptr);
//Return value: 0 if successful; Otherwise, the error number is returned

11.6.4 read write lock

  • There are three states of read-write lock: lock in read mode, lock in write mode and no lock. Only one thread can occupy the read-write lock of write mode at a time, but multiple threads can occupy the read-write lock of read mode at the same time.
  • However, when a thread attempts to acquire a lock in write mode, the read-write lock will block the subsequent read-write lock requests to avoid long-term occupation of the read-write lock.
  • Compared with mutexes, read-write locks must be initialized before use and destroyed before releasing their underlying memory.
#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);
//Return values of two functions: 0 if successful; Otherwise, the error number is returned
  • Pthread can also be used_ RWLOCK_ Initializer constant initializes statically allocated read / write locks
  • To lock the read-write lock in read mode, pthread needs to be called_ rwlock_ rdlock; To lock the read-write lock in write mode, pthread needs to be called_ rwlock_ wrlock. Pthread is called no matter how the read-write lock is locked_ rwlock_ Unlock to unlock
#include <pthread.h>
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
//Return value: 0 if successful; Otherwise, the error number is returned
  • Conditional version of read / write lock primitive:
#include <pthread.h>
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
//Return value: 0 is returned when the lock can be obtained; Otherwise, the error EBUSY is returned

11.6.5 read / write lock with timeout

#include <pthread.h>
#include <time.h>
int pthread_rwlock_timedrdlock(pthread_rwlock_t *restrict rwlock, const struct timespec *restrict tsptr);
int pthread_rwlock_timedwrlock(pthread_rwlock_t *restrict rwlock, const struct timespec *restrict tsptr);
//Return value: 0 if successful; If timeout occurs, ETIMEOUT is returned

11.6.6 conditional variables

  • Condition variables are another synchronization mechanism available to threads. Condition variables are protected by mutex, allowing threads to wait for specific conditions without competition.
  • There are two basic operations on condition variables:
  1. Wait: a thread is waiting on the condition variable because the wait condition is true. At this time, the thread will not occupy the mutex (the mutex should be locked before the wait condition, and the mutex should be locked during the waiting process
  2. Unlock, wait until the function returns (condition changes or timeout), and the mutex is locked again)
    Notification: when another thread makes the condition true, it notifies the waiting thread of the condition variable (it does not need to occupy the mutex when sending a signal to the waiting thread)
  • pthread_cond_t represents a condition variable. During initialization, the constant pthread can be used_ COND_ Initializer is assigned to statically allocated condition variables. If the condition variables are dynamically allocated, pthread needs to be called_ cond_ The init function initializes it.
#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);
//Return value: 0 if successful; Otherwise, the error number is returned
  • Using pthread_cond_wait wait condition variable is true
#include <pthread.h>
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 tsptr);
//Return value: 0 if successful; Otherwise, the error number is returned
  • There are two functions that can be used to notify a thread that a condition has been met. pthread_ cond_ The signal function can wake up at least one thread waiting for the condition, and pthread_ cond_ The broadcast function can wake up all threads waiting for the condition.
#include <pthread.h>
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);
//Return value: 0 if successful; Otherwise, the error number is returned

11.6.7 spin lock

  • Spinlock is similar to mutex, but it does not block the process through sleep, but is in a busy blocking state until the lock is obtained.
  • pthread_spinlock_t represents spin lock. The initialization and de initialization functions are:
int pthread_spin_init(pthread_spinlock_t *lock, int pshared);
int pthread_spin_destroy(pthread_spinlock_t *lock);
//Return value: 0 if successful; Otherwise, the error number is returned
  • The pshared parameter represents the process sharing attribute and indicates how the spin lock is obtained. If it is set to PTHREAD_PROCESS_SHARED, the spin lock can be obtained by threads that can access the underlying memory of the lock, even if those threads belong to different processes. Otherwise, the pshared parameter is set to PTHREAD_PROCESS_PRIVATE, the spin lock can only be accessed by threads inside the process initializing the lock.
  • You can use pthread_spin_lock and pthread_ spin_ The trylock function locks the spin lock. The former spins all the time before obtaining the lock. If the latter cannot obtain the lock, it immediately returns EBUSY error. Call pthread_spin_unlock function:
#include <pthread.h>
int pthread_spin_lock(pthread_spinlock_t *lock);
int pthread_spin_trylock(pthread_spinlock_t *lock);
int pthread_spin_unlock(pthread_spinlock_t *lock);
//Return value: 0 if successful; Otherwise, the error number is returned

11.6.8 barriers

  • The barrier allows any number of threads to wait until all threads finish processing, and all threads can continue to work after reaching the barrier.
  • Initialization and de initialization
#include <pthread.h>
int pthread_barrier_init(pthread_barrier *restrict barrier, const pthread_barrierattr_t *restrict attr, unsigned int count);
int pthread_barrier_destroy(pthread_barrier *barrier);
//Return value: 0 if successful; Otherwise, the error number is returned
  • The count parameter specifies the number of threads that must reach the barrier before allowing all threads to continue running.
  • Using pthread_barreir_wait function to indicate that the thread has completed its work and is ready to wait for all other threads to catch up.
#include <pthread.h>
int pthread_barrier_wait(pthread_barrier_t *barrier);
//Return value: 0 or pthread if successful_ BARRIER_ SERIAL_ THREAD; Otherwise, the error number is returned

Topics: Unix