In depth analysis of the usage of pthread_cond_wait of

  • 2020-04-02 01:23:03
  • OfStack

I haven't seen APUE for a long time. Today, a friend asked me a question about a mutex. He went back to the things we discussed before.
Take a quick look at the pthread_cond_wait() instructions found in many places on the web:

Condition variables,      
The condition variable is a mechanism of synchronization by using the Shared global variables between threads. It mainly consists of two actions: one thread waits for the condition of the condition variable to hold; The other thread makes the condition true. To prevent contention, the use of condition variables is always combined with a mutex.  

1. Create and log out    
The condition variable, like the mutex, has two static and dynamic creation methods. The static method USES the constant PTHREAD_COND_INITIALIZER, as follows:        

pthread_cond_t   cond=PTHREAD_COND_INITIALIZER     
     
Call the pthread_cond_init() function in dynamic mode. The API is defined as follows:        

int   pthread_cond_init(pthread_cond_t   *cond,   pthread_condattr_t   *cond_attr)    
     
Although attributes are defined for conditional variables in the POSIX standard, they are not implemented in LinuxThreads, so the cond_attr value is usually NULL and ignored.    

To log out a condition variable requires a call to pthread_cond_destroy(), which can only be done if there are no threads waiting on the condition variable, otherwise EBUSY is returned. Because the Linux implementation of the condition variable does not allocate any resources, the logout action consists only of checking for waiting threads. API definition is as follows:      

int   pthread_cond_destroy(pthread_cond_t   *cond)     
     
2. Waiting and firing    
    
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)    

Waiting on the condition there are two ways: unconditional waiting pthread_cond_wait () and timing for the pthread_cond_timedwait (), in which timing wait way if the conditions are not met, before the given time returns ETIMEOUT, wait for the end of the abstime with time () system call absolute time form the same meaning, 0 means GMT on January 1, 1970 0 0 0 seconds.  

Either way, a mutex must be coupled to prevent multiple threads from simultaneously requesting a Race condition (Race) for pthread_cond_wait() (or pthread_cond_timedwait(), the same below)     Condition). The mutex mutex must be either a normal lock(PTHREAD_MUTEX_TIMED_NP) or an adaptive lock(PTHREAD_MUTEX_ADAPTIVE_NP), and must be locked by the thread itself (pthread_cond_wait()) before calling the pthread_cond_wait(), while the mutex remains locked until the update condition waits queue and unlocks before the thread hangs into the wait. Before the condition is satisfied to leave the pthread_cond_wait(), the mutex is relocked to correspond to the locking action before entering the pthread_cond_wait().    

Pthread_cond_signal () activates a thread waiting for the condition, and when there are multiple waiting threads, activates one of them in queue order. Pthread_cond_broadcast () activates all wait threads.  

Now let's look at a typical application: just look at the comments.

#include <pthread.h>
#include <unistd.h>
static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
struct node {
int n_number;
struct node *n_next;
} *head = NULL;

static void cleanup_handler(void *arg)
{
    printf("Cleanup handler of second thread./n");
    free(arg);
    (void)pthread_mutex_unlock(&mtx);
}
static void *thread_func(void *arg)
{
    struct node *p = NULL;
    pthread_cleanup_push(cleanup_handler, p);
    while (1) {
    pthread_mutex_lock(&mtx);           //This mutex is mainly used to ensure the concurrency of pthread_cond_wait
    while (head == NULL)   {               //While, in particular, is perfectly fine with a single pthread_cond_wait, so why have a while (head == NULL)? Because threads in pthread_cond_wait can be accidentally awakened if the head! = NULL, which is not the case. At this point, you should let the thread continue into the pthread_cond_wait
        pthread_cond_wait(&cond, &mtx);         //Pthread_cond_wait unlocks the MTX of the previous pthread_mutex_lock lock, then blocks the sleep in the wait column until it wakes up again (most of the time, the wait condition is true and the process locks the pthread_mutex_lock(& MTX). , and then read the resource
                                                //This process is relatively clear
    }
        p = head;
        head = head->n_next;
        printf("Got %d from front of queue/n", p->n_number);
        free(p);
        pthread_mutex_unlock(&mtx);             //Critical section data operation is completed, release the mutex
    }
    pthread_cleanup_pop(0);
    return 0;
}
int main(void)
{
    pthread_t tid;
    int i;
    struct node *p;
    pthread_create(&tid, NULL, thread_func, NULL);   //Child threads wait for resources, like producers and consumers, but the consumer can be multiple consumers, not just a single consumer. This is a simple but powerful model
    
    for (i = 0; i < 10; i++) {
        p = malloc(sizeof(struct node));
        p->n_number = i;
        pthread_mutex_lock(&mtx);             //I need to manipulate the critical resource head, lock it first,
        p->n_next = head;
        head = p;
        pthread_cond_signal(&cond);
        pthread_mutex_unlock(&mtx);           //unlock
        sleep(1);
    }
    printf("thread 1 wanna end the line.So cancel thread 2./n");
    pthread_cancel(tid);             //A little extra about pthread_cancel is that it terminates the child thread externally, and the child thread exits the thread at the nearest cancellation point, which in our code must be pthread_cond_wait(). About the cancellation point information, interested can be Google, here is not to say
    pthread_join(tid, NULL);
    printf("All done -- exiting/n");
    return 0;
}

Related articles: