Anyone who has worked on a slightly larger project knows that they strive for program stability and convenient scheduling and use a large number of threads. Almost every module has a dedicated thread processing function. The mutex and condition variables are essential in thread management, and scheduling between tasks is almost controlled by the mutex and condition variables. The implementation of mutex is similar to that of semaphores in processes. Of course, semaphores can also be used in threads. The difference is that during initialization, semaphores are essentially P/V Operations. During compilation, remember to add-lpthread or-lrt.
RelatedInter-process communication (Message Queue)See:In-depth Message Queue explanation for inter-process communication
I. mutex
1. Initialization and destruction:
ForStatic allocationCan be initializedPTHREAD_MUTEX_INITIALIZER(EquivalentPthread_mutex_init (..., NULL ))OrCall pthread_mutex_init.
ForDynamic AllocationThe amount of mutex. After applying for memory (malloc), it is initialized through pthread_mutex_init, and pthread_mutex_destroy needs to be called before releasing the memory (free.
Int pthread_mutex_init (pthread_mutex_t * restrict mutex, const pthread_mutexattr_t * restric attr );
Int pthread_mutex_destroy (pthread_mutex_t * mutex );
Return Value:If the call succeeds, 0 is returned. If the call fails, the error number is returned.
Note:1. If the default attribute is used to initialize mutex,Set attr to NULL..
2. Destroying a mutex lock means releasing the resources it occupies and requiring the lock to be open. In Linux, mutex locks do not occupy any resources. Therefore, pthread_mutex_destroy () in LinuxThreads has no action except checking the lock status (EBUSY is returned when the lock status is locked.
2. mutex operation:
To access shared resources, You need to lock the mutex. If the mutex has been locked, the calling thread will be blocked until the mutex is unlocked. After accessing shared resources, unlock the mutex.
Int pthread_mutex_lock (pthread_mutex_t * mutex); // P operation: Request Resource (+ 1)
Int pthread_mutex_trylock (pthread_mutex_t * mutex );
Int pthread_mutex_unlock (pthread_mutex_t * mutex); // V operation: release resources (-1)
Return Value:If the call succeeds, 0 is returned. If the call fails, the error number is returned.
Note:1. to lock a mutex, call pthread_mutex_lock. If mutex is locked, the called thread will be blocked until the semaphore is unlocked.
2. For details, refer to the trylock function. This function isNon-blockingCall mode. That is to say, if the mutex is not locked, the trylock function locks the mutex and obtains access to the shared resources. If the mutex is locked, the trylock function does not block the wait and returns EBUSY directly, indicating that the shared resources are busy.
3. to unlock a semaphore, we call phtread_mutex_unlock.
3. deadlock, synchronization, and mutex
3.1 deadlock:
Sometimes, you may need to access two resources at the same time. You may be using one of the resources, and then find that another resource is required. If the two threads attempt to declare these two resources, but lock the mutex associated with these resources in different order, the problem may occur. For example, if two threads lock mutex lock 1 and mutex lock 2 respectively, a deadlock occurs when each thread attempts to lock the other mutex lock. The following example illustrates possible deadlocks.
Thread 1 |
Thread 2 |
Pthread_mutex_lock (& m1 ); Pthread_mutex_lock (& m2 ); Do something ...... Pthread_mutex_unlock (& m2 ); Pthread_mutex_unlock (& m1 ); |
Pthread_mutex_lock (& m2 ); Pthread_mutex_lock (& m1 ); Do something ...... Pthread_mutex_unlock (& m1 ); Pthread_mutex_unlock (& m2 ); |
3.2 synchronization:
Thread 1 |
Thread 2 |
Pthread_mutex_lock (& m1 ); Do something ...... Pthread_mutex_unlock (& m2 ); |
Pthread_mutex_lock (& m2 ); Do something ...... Pthread_mutex_unlock (& m1 ); |
3.3 mutex:
Thread 1 |
Pthread_mutex_lock (& m1 ); Do something ...... // Critical Section) Pthread_mutex_unlock (& m1 ); |
4. Summary of mutex
1. You must obtain the lock before operating on shared resources.
2. Release the lock after completing the operation.
3. Use the lock as soon as possible.
4. If there are multiple locks, for example, if the order is ABC, the release order should also be ABC.
5. When a thread returns an error, it should release the lock it obtained.
Ii. Condition Variables
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, for example:Pthread_cond_t cond = PTHREAD_COND_INITIALIZER
B. Dynamic Mode
Int pthread_cond_init (pthread_cond_t * cond, pthread_condattr_t * cond_attr)
Use the attribute specified by cond_attr to initialize the condition variable cond. When cond_attr is NULL, use the default attribute. The threads implementation condition variable does not support attributes, so the cond_attr parameter is ignored.
C.Cancel
Int pthread_cond_destroy (pthread_cond_t * cond)
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.
2. Waiting and stimulating
2.1 waiting
Int pthread_cond_wait (pthread_cond_t * cond, pthread_mutex_t * mutex)
This function isPOSIXCore of the thread signal sending system, TooLeast understandableThe process is as follows:Unlock-wait-receive signal-lock-return.
2.2 set the wait time
Int pthread_cond_timedwait (pthread_cond_t * cond, pthread_mutex_t * mutex, conststruct timespec * abstime)
Pthread_cond_timedwaitSame as pthread_cond_wait, Automatically unlock mutex and wait condition variables, but it also limits the waiting time. If cond is not triggered within the time specified by abstime, mutex is re-locked and an error ETIMEDOUT is returned. The abstime parameter specifies an absolute time. The time origin is the same as time and gettimeofday: abstime = 0 indicates 00:00:00 GMT on January 1, January 1, 1970.
2.3 Excitation
Int pthread_cond_signal (pthread_cond_t * cond );
Int pthread_cond_broadcast (pthread_cond_t * cond );
There are two types of excitation conditions: pthread_cond_signal ()Activate a thread waiting for this conditionWhen multiple threads are blocked on this condition variable, which thread is awakened by the threadScheduling PolicyAnd pthread_cond_broadcast ()Activate all waiting threadsAfter these threads are awakened, they will compete for the corresponding mutex lock again.
Note that the mutex lock of the Protection Condition variable must be used to protect the activation function. Otherwise, the signal meeting the condition may be sent between the test condition and the call of the pthread_cond_wait () function, this causes unlimited waiting.
Iii. mutex and condition Variables
The problem of mutex: essentially, mutex is a lock, and the mutex is executed in a serial mode.Make sure that only one thread is accessed at a time..The mutex is a thread program.Necessary tools, but they are not omnipotent. For example, if the thread is polling and waiting for a condition in the shared data to appear, what will happen? It can repeatedly lock and unlock mutex objects. Each time it checks the shared data structure to find a value. However, this is a waste of time and resources, and the efficiency of such busy queries is very low. Similarly, the thread enters sleep for a short period of time between each check, for example, 3 s, but the thread Code cannot respond as quickly as possible.
Solution: conditional variablesBy allowing threadsBlockingAnd wait for another thread to send a signal to make up for the lack of mutex lock,Conditional variables 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.
4. Thread Management Code
Copy codeThe Code is as follows: // omitting the thread mutex and the initialization of conditional variables
// Thread management: Blocking sec second read thread Information
// The three parameters are thread information, thread ID, and timeout seconds.
Bool ManagePthread_TimeReadSignal (PTHREAD_BUF * rbuf, PTHREAD_ID thread_num, int sec)
{
Bool B _valid = false;
Struct timespec;
Int err;
To. TV _sec = time (NULL) + sec;
To. TV _nsec = 0;
// Lock
Pthread_mutex_lock (& managePthread. g_pthread_mutex [thread_num]);
// Timeout sec seconds blocking wait, similar to select
Err = pthread_cond_timedwait (& managePthread. g_pthread_cond [thread_num], & managePthread. g_pthread_mutex [thread_num], & );
If (err = ETIMEDOUT)
{
Pthread_mutex_unlock (& managePthread. g_pthread_mutex [thread_num]);
Return false;
}
// Obtain thread Information
If (managePthread. g_pthread_info [thread_num] = WRITE_FLAG)
{
ManagePthread. g_pthread_info [thread_num] = READ_FLAG;
Memcpy (PTHREAD_BUF *) rbuf, (PTHREAD_BUF *) & managePthread. g_pthread_buf [thread_num], sizeof (PTHREAD_BUF ));
B _valid = true;
}
// Unlock
Pthread_mutex_unlock (& managePthread. g_pthread_mutex [thread_num]);
Return B _valid;
}
// Block reading thread Information
Bool ManagePthread_ReadSignal (PTHREAD_BUF * rbuf, PTHREAD_ID thread_num, bool wait)
{
Bool B _valid = false;
Pthread_mutex_lock (& managePthread. g_pthread_mutex [thread_num]);
If (wait = true)
Pthread_cond_wait (& managePthread. g_pthread_cond [thread_num], & managePthread. g_pthread_mutex [thread_num]);
If (managePthread. g_pthread_info [thread_num] = WRITE_FLAG)
{
ManagePthread. g_pthread_info [thread_num] = READ_FLAG;
Memcpy (PTHREAD_BUF *) rbuf, (PTHREAD_BUF *) & managePthread. g_pthread_buf [thread_num], sizeof (PTHREAD_BUF ));
B _valid = true;
}
Pthread_mutex_unlock (& managePthread. g_pthread_mutex [thread_num]);
Return B _valid;
}
// Activate/send thread Information
Bool ManagePthread_SendSignal (PTHREAD_BUF * sbuf, PTHREAD_ID thread_num)
{
Bool B _valid = false;
Pthread_mutex_lock (& managePthread. g_pthread_mutex [thread_num]);
ManagePthread. g_pthread_info [thread_num] = WRITE_FLAG;
If (sbuf)
{
Memcpy (PTHREAD_BUF *) & managePthread. g_pthread_buf [thread_num], (PTHREAD_BUF *) sbuf, sizeof (PTHREAD_BUF ));
}
Pthread_mutex_unlock (& managePthread. g_pthread_mutex [thread_num]);
Pthread_cond_signal (& managePthread. g_pthread_cond [thread_num]);
B _valid = true;
Return B _valid;
}
// Broadcast
Bool ManagePthread_BroadcastSignal (PTHREAD_BUF * sbuf, PTHREAD_ID thread_num)
{
Bool B _valid = false;
Pthread_mutex_lock (& managePthread. g_pthread_mutex [thread_num]);
ManagePthread. g_pthread_info [thread_num] = WRITE_FLAG;
Memcpy (PTHREAD_BUF *) & managePthread. g_pthread_buf [thread_num], (PTHREAD_BUF *) sbuf, sizeof (PTHREAD_BUF ));
Pthread_mutex_unlock (& managePthread. g_pthread_mutex [thread_num]);
Pthread_cond_broadcast (& managePthread. g_pthread_cond [thread_num]);
B _valid = true;
Return B _valid;
}