Mutex in Pthread

Mutex is a lock that could be used to prevent threads from reading from and/or writing to the variable that they share between each other. This could prevent the #202112061109 from happening since the program will be able to deny granting access of that variable to other threads when the mutex is currently locked by a thread. Such mechanism is great for code synchronisation.

In pthread, the creation of mutex could be done by declaring a global pthread_mutex_t variable and initialise it by calling pthread_mutex_init. To lock it, call pthread_mutex_lock onto the mutex, and to unlock, call pthread_mutex_unlock onto the mutex. At the end, we need to destroy the mutex from the program by calling pthread_mutex_destroy.

pthread_mutex_t mutex;

// in the program
pthread_mutex_init(&mutex, NULL);

// at somewhere
pthread_mutex_lock(&mutex);
pthread_mutex_unlock(&mutex);

// at the end of the program
pthread_mutex_destroy(&mutex);

Trylock

Instead of using pthread_mutex_lock, one could use pthread_mutex_trylock to avoid threads from waiting. When pthread_mutex_trylock is called, the thread will first try to obtain the mutex lock for itself. If that fail, the function will return non-zero value to indicate that the current mutex is busy, and the thread will execute its remaining content, of course without the content locked by the mutex. If it is a success, the function will return 0. Therefore, checking the return value of pthread_mutex_trylock is necessary.

if (pthread_mutex_trylock(&mutex) == 0) {
  // locked content
  pthread_mutex_unlock(&mutex);
}

// remaining content

Recursive Lock

When the thread need to lock the mutex for multiple times, it is wise to use the recursive lock in order to prevent 202202191853. Since the default initialisation of mutex does not allow us to recursively lock it, we need to modify its attribute. To do that, we need to declare a pthread_mutexattr_t and then initialise it by calling pthread_mutexattr_init. Set the correct type by calling pthread_mutexattr_settype. When calling on pthread_mutex_init, pass it to its second parameter. Later on, clean it up using pthread_mutexattr_destroy.

pthread_mutexattr_t attr;

pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&mutex, &attr);
pthread_mutexattr_destroy(&attr);

Static Initialiser

Mutex could be statically initialised without using pthread_mutex_init in order to acquire the necessary resources for itself. This could be a good shorthand for the user to keep this in their repertoire.

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

Although pthread_mutex_destroy has become optional in this case, it is a good practice to keep it as it is much clearer where does the resources have been cleanup.

Fine Control Threads Execution’s Sequence

See 202112132216#

Links to this page
  • Semaphore in Pthread

    Semaphore is another way to lock a critical section other than mutex which could prevent the #202112061109. It could be used to restrict the number of possible simultaneous accesses to limited amount of resources. The difference between semaphore and mutex is the thread that lock the mutex own the mutex for its entire lifetime. Semaphore does not introduce ownership to the threads that lock or decrement it.

    The syntax to create, initialise and destroy semaphore is similar to mutex. The keywords are sem_t, sem_init, and sem_destroy. There are two semaphore operations: sem_wait and sem_post. sem_wait is used to check if the semaphore is greater than 0. It will proceed if so, wait in there if not until the value of semaphore is greater than 0. sem_post need to be called after the critical section in order to release the occupied semaphore by incrementing its value. Note their similarity to the pthread_mutex_lock and pthread_mutex_unlock.

    Unlike mutex, the library for semaphore is not couple into <pthread.h>. Instead, we should import it from <semaphore.h>.

  • Producer-Consumer Problem
    Safeguard the shared memory by implementing mutex.
  • Multithreading Management
  • Mach Memory Management

    For shared memory (only threads are able to share memory), Mach uses 202112061117# and critical sections to synchronised. If the memory is shared between two different machines, an external memory manager should be used (Mach provide Network Memory Server, NetMemServer).

  • Lock-Guard

    Lock-Guard as std::lock_guard in C++ standard library is a #RAII object that operates similarly with 202112061117.

  • Conditional Variable in Pthread

    Conditional variable is used alongside with #mutex to have fine control of the threads’ execution’s sequence. This could improve the synchronisation of all the threads as they could continue their execution only when they have been signalled to do so.

    In pthread, before using the conditional variable, initialise it by creating an object of pthread_cond_t and call the function pthread_cond_init on it. Note the similar syntax it shares with #mutex. In order to order (pun intended) the thread to wait in line rather than just continue its execution, we need to call pthread_cond_wait with a mutex that pass into it. After that, to destroy a pthread_cond_t object, call pthread_cond_destroy at the end of the program.

    Just like #mutex, conditional variable could use static initialiser to initialise itself in order to reduce the number of code.

  • Barrier in Pthread

    Note: This is much different to 202112061117 since it does not actually lock down a certain portion of codes.

#multithreading #pthread