Differences between semaphore and mutex:
Scope
Semaphore: process or thread (Linux only)
Mutex lock: between threads
Lock
Semaphores: as long as the semaphores value is greater than 0, other threads can successfully sem_wait, And the semaphores value minus one after success. If the value is not greater than 0, sem_wait is blocked until the value of sem_post is added after the release of sem_post.
Mutex lock: as long as it is locked, no other thread can access the protected resources. Otherwise, it will be blocked.
I. mutex lock
Mutex lock is a semaphore that is often used to prevent two processes or threads from accessing the same shared resources at the same time.
Required header file: pthread. h
Mutex identifier: pthread_mutex_t
(1) mutex lock initialization:
Function prototype: int pthread_mutex_init (pthread_mutex_t * mutex, const pthread_mutexattr_t * mutexattr );
Function input value: mutex.
Mutexattr: pthread_mutex_initializer to create a quick mutex lock.
Pthread_recursive_mutex_initializer_np creates a recursive mutex lock.
Pthread_errorcheck_mutex_initializer_np create an error mutex lock.
Function return value: Success: 0; error:-1
(2) mutex operation functions
Int pthread_mutex_lock (pthread_mutex_t * mutex); // lock
Int pthread_mutex_trylock (pthread_mutex_t * mutex); // The lock is blocked only when the mutex is locked.
Int pthread_mutex_unlock (pthread_mutex_t * mutex); // unlock
Int pthread_mutex_destroy (pthread_mutex_t * mutex); // clear the mutex lock
Function input value: mutex.
Function return value: Success: 0; error:-1
Usage:
Pthread_mutex_t mutex;
Pthread_mutex_init (& mutex, null);/* definition */
...
Pthread_mutex_lock (& mutex);/* obtain the mutex lock */
.../* Critical resource */
Pthread_mutex_unlock (& mutex);/* release mutex lock */
If a thread has locked a mutex and then calls the lock operation again during the operation, the thread will be blocked infinitely, this leads to deadlocks. This requires the mutex attribute.
The mutex can be divided into the following three types:
1. Fast. This type is also the default type. The thread behavior is as mentioned above.
2. Recursive type. If the deadlock we mentioned above occurs and the same thread loops to lock mutex, the system will know that the lock behavior comes from the same thread, then the thread will agree to lock the mutex.
3. Error Detection type. If the mutex has been locked, subsequent locks will fail without blocking. The pthread_mutex_lock () operation will return edeadlk.
The type of the mutex attribute is pthread_mutexattr_t. Call pthread_mutexattr_init () to create the mutex. Call pthread_mutexattr_settype to set properties. Format: int pthread_mutexattr_settype (pthread_mutexattr_t * ATTR, int kind );
The first parameter ATTR is the previously declared attribute variable. The second parameter kind is the attribute type we want to set. He has the following options:
Pthread_mutex_fast_np
Pthread_mutex_recursive_np
Pthread_mutex_errorcheck_np
The following is a simple process of using attributes:
Pthread_mutex_t mutex;
Pthread_mutexattr_t ATTR;
Pthread_mutexattr_init (& ATTR );
Pthread_mutexattr_settype (& ATTR, pthread_mutex_recursive_np );
Pthread_mutex_init (& mutex, & ATTR );
Pthread_mutex_destroy (& ATTR );
As mentioned above, when calling pthread_mutex_lock (), if mutex is locked by other threads, this operation will be blocked all the time. If we don't want to block this place all the time, we can call the following function: pthread_mutex_trylock.
If the mutex is not locked, pthread_mutex_trylock returns 0 and locks the mutex. If the mutex is locked, ebusy is returned immediately.
Ii. Condition Variables
Required header file: pthread. h
Condition variable identifier: pthread_cond_t
1. mutex lock problems:
An obvious disadvantage of mutex is that it has only two States: Locked and non-locked. Imagine a simple scenario: when multiple threads access the same shared resource, they do not know when to use the shared resource. If a judgment statement is added to the critical section, it may be effective, however, the efficiency is not high, and it is difficult to compile in a complex environment. This is a structure that can trigger the corresponding thread at the time of condition establishment for variable modification and access.
2. Condition variables:
Conditional variables make up for the lack of mutex lock by allowing the thread to block and waiting for another thread to send signals. They are often used together with mutex locks. When a condition variable is used to block a thread, when the condition is not met, the thread often unlocks the corresponding mutex and waits for the condition to change. Once another thread changes the condition variable, it will notify the corresponding condition variable to wake up one or more threads that are blocked by this condition variable. These threads will re-lock the mutex and re-test whether the conditions are met.
3. functions related to conditional variables
Pthread_cond_t cond = pthread_cond_initializer; // Conditional Variable Structure
Int pthread_cond_init (pthread_cond_t * cond, pthread_condattr_t * cond_attr );
Int pthread_cond_signal (pthread_cond_t * Cond );
Int pthread_cond_broadcast (pthread_cond_t * Cond );
Int pthread_cond_wait (pthread_cond_t * cond, pthread_mutex_t * mutex );
Int pthread_cond_timedwait (pthread_cond_t * cond, pthread_mutex_t * mutex,
Const struct timespec * abstime );
Int pthread_cond_destroy (pthread_cond_t * Cond );
Detailed description:
(1) Create and log out
Like mutex lock, conditional variables have two static and dynamic creation methods.
A. static mode
Use the pthread_cond_initializer constant in static mode, as follows:
Pthread_cond_t cond = pthread_cond_initializer
B. Dynamic Mode
Call the pthread_cond_init () function dynamically. The API is defined as follows:
Int pthread_cond_init (pthread_cond_t * cond, pthread_condattr_t * cond_attr)
Although the POSIX standard defines attributes for condition variables, they are not implemented in linuxthreads. Therefore, the cond_attr value is usually null and ignored.
To cancel a condition variable, you must call pthread_cond_destroy (). This condition variable can be canceled only when no thread is waiting for the condition variable. Otherwise, ebusy is returned. Because the condition variables implemented in Linux do not allocate any resources, the logout action only includes checking whether there are waiting threads. The API is defined as follows: int pthread_cond_destroy (pthread_cond_t * Cond)
(2) waiting and stimulating
A. Wait
Int pthread_cond_wait (pthread_cond_t * cond, pthread_mutex_t * mutex) // wait
Int pthread_cond_timedwait (pthread_cond_t * cond, pthread_mutex_t * mutex,
Const struct timespec * abstime) // sometimes wait
There are two waiting conditions: pthread_cond_wait () and pthread_cond_timedwait (). If the waiting time is not met before the given time, etimeout is returned and the waiting time ends, here, abstime appears in the absolute time format of the same meaning as the time () system call. 0 indicates GMT 00:00:00, January 1, January 1, 1970.
Either way, you must work with a mutex lock to prevent multiple threads from simultaneously requesting the Race Condition of pthread_cond_wait () (or pthread_cond_timedwait (), the same below ). Mutex locks must be common locks (pthread_mutex_timed_np) or adaptive locks (pthread_mutex_adaptive_np), and must be locked by this thread before pthread_cond_wait () is called ()), before the update condition is waiting for the queue, mutex remains locked and is unlocked before the thread is suspended and enters the waiting state. Leave when conditions are met
Before pthread_cond_wait (), mutex will be re-locked to match the lock action before pthread_cond_wait.
B. Excitation
There are two ways to activate the condition. pthread_cond_signal () activates a thread waiting for the condition. When there are multiple waiting threads, one of them is activated in the queue order; and pthread_cond_broadcast () activate all the waiting threads.
(3) Other operations
Pthread_cond_wait () and pthread_cond_timedwait () are both implemented as cancellation points. Therefore, the thread waiting for this point will immediately run again and leave pthread_cond_wait () after re-locking mutex (), then, cancel the operation. That is to say, if pthread_cond_wait () is canceled, mutex remains locked. Therefore, you need to define the exit callback function to unlock it.
Pthread_cond_wait can be considered as a combination of the following actions:
Unlock the thread lock;
The waiting condition is true;
Lock the thread lock;
Usage:
// Thread-1 code
Pthread_mutex_lock (& mutex );
If (condition satisfied)
Pthread_cond_signal (& Cond );
Pthread_mutex_unlock (& mutex );
// Thread 2 code
Pthread_mutex_lock (& mutex );
While (the condition is not met)
Pthread_cond_wait (& cond, & mutex );
Pthread_mutex_unlock (& mutex );
/* Why does thread 2 use while? Because there is a time difference between pthread_cond_signal and pthread_cond_wait return, it is assumed that the condition has changed during the time difference, and it is clear that you need to re-check the condition. That is to say, when pthread_cond_wait is awakened, this condition may not be true. */
3. semaphores
Semaphores are actually a counter and an integer. Each wait operation will reduce the semaphore value by one. If the semaphore value is already 0, the wait operation will be blocked. Each call to the post operation will add the semaphore value to one.
Required header file: semaphore. h
Semaphore identifier: sem_t
Main functions:
(1) sem_init
Function: Creates a semaphore and initializes the semaphore value.
Function prototype: int sem_init (sem_t * SEM, int pshared, unsigned int value );
Function input value: SEM: semaphore.
Pshared: determines whether the semaphore can be shared among several processes. Currently, Linux does not share information between processes, so this value can only be 0.
Value: initial Calculator
Function return value: 0: Successful;-1: failed.
(2) other functions.
// Waiting for semaphores
Int sem_wait (sem_t * SEM );
Int sem_trywait (sem_t * SEM );
// Send semaphores
Int sem_post (sem_t * SEM );
// Obtain the signal value
Int sem_getvalue (sem_t * SEM );
// Delete semaphores
Int sem_destroy (sem_t * SEM );
Function: sem_wait and sem_trywait are equivalent to P operations. Both of them can reduce the semaphore value by one. The difference between them is that if the semaphore value is smaller than zero, sem_wait will block the process, sem_trywait returns immediately.
Sem_post is equivalent to the V operation. It adds the semaphore value and sends a wake-up signal to the waiting process (or thread ).
Sem_getvalue to obtain the semaphore value.
Sem_destroy: destroy the semaphore.
Usage:
Sem_t SEM;
Sem_init (& SEM, 0, 1);/* semaphore initialization */
...
Sem_wait (& SEM);/* Waiting For semaphores */
.../* Critical resource */
Sem_post (& SEM);/* release semaphore */
Semaphores differ from thread locks and condition variables in the following ways:
1) The lock must be obtained and released by the same thread; otherwise, the lock will be deadlocked. Conditional variables and semaphores are not required.
2) the increase and decrease of signals will be automatically remembered by the system. There is a counter inside the system to implement the semaphores, so you don't have to worry about the loss. When you wake up a conditional variable, if no corresponding thread is waiting for this condition variable, this wake-up will be lost.