Linux condition variable pthread_cond_signal

  


Table of contents

foreword

Introduction

Application Scenario

Differences from Mutex/Semaphore

Interface introduction

Variable definitions

initialization

waiting to be woken up

wake up the waiters

memory cleaning

condition variable trap

lost wakeup

spurious wakeup

code demo

end


foreword

This column mainly shares knowledge about concurrent programming under Linux, including multi-process, multi-thread, communication between processes/threads, concurrent synchronization control, and performance improvement under high concurrency. Please leave a message.

Write an open source database from scratch, git links  toadb  , everyone pays more attention, welcome to add bricks and tiles~


Introduction

Condition variables are triggered according to a certain condition, and wait when the condition is not met, without us constantly checking.

Application Scenario

Differences from Mutex/Semaphore

The condition variable is to trigger the current thread/process to wake up when a certain variable changes to the current required condition; the mutex is to protect the shared variable/memory sequential read and write access, so that the shared content can be read or written correctly , without overwriting or dirty reading; the semaphore mainly controls multiple concurrent accesses to the shared area.

Of course, you can also use mutexes/semaphores to achieve the effect of condition variables, that is, to continuously acquire locks, judge conditions, release locks, wait, and repeat. In this way, first, it will waste CPU resources and run many times in idling; second, the waiting time is not easy to determine. If it is long, the sensitivity to condition processing will be reduced. If it is short, the CPU usage will be serious.

So referencing the condition variable is similar to event triggering. When the condition is met, the waiting thread/process will be notified to continue execution.

Interface introduction

  • Variable definitions

#include <pthread.h>

pthread_cond_t myCond;

  • initialization

There are two ways to initialize condition variables,

One is to directly assign PTHREAD_COND_INITIALIZER to the condition variable, for example:

pthread_cond_t myCond = PTHREAD_COND_INITIALIZER;

Condition variables can also be initialized with the help of the pthread_cond_init() function, the syntax format is as follows:

int pthread_cond_init(pthread_cond_t * cond, const pthread_condattr_t * attr);

The parameter cond is used to indicate the condition variable to be initialized; the parameter attr is used to customize the properties of the condition variable, usually we assign it a value of NULL, which means that the initialization operation is completed with the system default properties.

The pthread_cond_init() function returns the number 0 when the initialization is successful, otherwise the function returns a non-zero number.

When the attr parameter is NULL, the above two initialization methods are completely equivalent.

  • waiting to be woken up

When the condition is not true, the condition variable can block the current thread, and all blocked threads will form a waiting queue.

Blocking threads can be implemented with the help of the following two functions:

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);

The cond parameter represents the initialized condition variable; the mutex parameter represents the mutex used in conjunction with the condition variable; the abstime parameter represents the time of blocking the thread.

Note that the abstime parameter refers to the absolute time. For example, if you intend to block the thread for 5 seconds, you must first obtain the current system time, and then add 5 seconds, and the final time is the passed actual parameter value.

Before calling the two functions, we must first create a mutex and complete the "locking" operation, and then pass it to the mutex parameter as an actual parameter. The two functions do the following two things:

Block the thread until a "condition is met" signal is received;

When a thread is added to the waiting queue, the mutex is "unlocked".

That is to say, until the function has not received the "conditional" signal, it will block the thread execution.

Note that when the function receives the "condition is established" signal, it does not end the blocking of the thread immediately, but completes the "locking" operation of the mutex first, and then unblocks it.

Both functions complete the two processes of "blocking thread + unlocking" or "re-locking + unblocking" in an "atomic operation" manner. The so-called "atomic operation" means that when multiple threads execute the same process, although they all access the mutex and condition variables, they will not interfere with each other.

The above two functions can be used to block the thread, their difference is: the pthread_cond_wait() function can block the thread permanently until the moment when the condition variable is established; the pthread_cond_timedwait() function can only block the thread within the time specified by the abstime parameter, exceeding After the time limit, the function will re-execute the "lock" operation on the mutex and unblock the thread. The return value of the function is ETIMEDOUT.

If the function successfully receives the "condition is satisfied" signal, re-locks the mutex and makes the thread continue to execute, the function returns the number 0, otherwise it returns a non-zero number.

The POSIX standard states that the pthread_cond_wait() and pthread_cond_timedwait() functions are functions that can act as "cancellation points".

  • wake up the waiters

For threads blocked by the pthread_cond_wait() or pthread_cond_timedwait() function, we can use the following two functions to send them a "conditional" signal to release their "blocked" state:

int pthread_cond_signal(pthread_cond_t* cond);

int pthread_cond_broadcast(pthread_cond_t* cond);

The cond parameter represents the initialized condition variable. When the function successfully releases the "blocked" state of the thread, it returns the number 0, otherwise it returns a non-zero number.

Both functions can release the "blocked" state of the thread, the difference is:

The pthread_cond_signal() function releases the "blocked" state of at least one thread. If there are multiple threads in the waiting queue, which thread to release first will be determined by the thread scheduler of the operating system;

The pthread_cond_broadcast() function can release the "blocked" state of all threads in the waiting queue.

Due to the existence of the mutex, the unblocked thread may not be able to execute immediately. When the mutex is in the "locked" state, all threads in the unblocked state will form a queue waiting for the mutex resource, waiting for the mutex to be "unlocked".

  • memory cleaning

For the initialized condition variable, we can call the pthread_cond_destory() function to destroy it.

The syntax of the pthread_cond_destory() function is as follows:

int pthread_cond_destroy(pthread_cond_t *cond);

The cond parameter indicates the condition variable to be destroyed. If the function successfully destroys the condition variable specified by the cond parameter, the number 0 is returned, otherwise a non-zero number is returned.

It is worth mentioning that the destroyed condition variable can also be used after reinitialization by calling the pthread_cond_init() function.

condition variable trap

  • lost wakeup

When the condition variable wakes up, the waiter has not been executed to wait; when the waiter is in the waiting state, the wakeup is equivalent to having already occurred, so in this case, the waiter can never be woken up.

  • spurious wakeup

There is no actual wakeup, and there are cases where the waiter may also be woken up.

code demo

#include <stdio.h>
#include <pthread.h>

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;   // 定义互斥锁
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;     // 定义条件变量


int car_wash_progress=0;

void *thread_host(void *arg) 
{
        printf("等待洗车店通知,我可以去打两把游戏了...\n");
        pthread_mutex_lock(&mutex);   // 加锁

        while(car_wash_progress < 100)  // 防止虚假唤醒
        {
            pthread_cond_wait(&cond, &mutex);    // 等待条件变量

            printf("车洗完了通知? \n");         
        }

        printf("车洗完了,我要开走了..\n");
        pthread_mutex_unlock(&mutex); // 解锁
    return NULL;
}

void *thread_CarWash(void *arg) 
{

    while(car_wash_progress < 100)
    {
        printf("洗车店洗车中..\n");
        pthread_mutex_lock(&mutex);   // 加锁
        car_wash_progress++;
        pthread_mutex_unlock(&mutex); // 解锁
    }

    printf("车洗完了,通知车主..\n");
    pthread_cond_signal(&cond);    // 等待条件变量

    return NULL;
}

int main() 
{
    pthread_t tid[2];
    printf("主线程正在创建子线程...\n");

    /* 先创建车主线程任务,避免唤醒丢失 */
    pthread_create(&tid[0], NULL, thread_host, NULL);    // 创建子线程
    pthread_create(&tid[1], NULL, thread_CarWash, NULL);    // 创建子线程

    pthread_join(tid[1], NULL);    // 等待子线程结束
    pthread_join(tid[0], NULL);
    return 0;
}

In the code, we deal with wake-up loss and false wake-up. In addition, when calling pthread_cond_signal, whether to add a mutex lock needs to be judged according to the business situation. If there are other tasks that also use the same mutex, it is recommended to put it in the mutex Signal in the middle.


end

Author email: [email protected]
If there are any mistakes or omissions, please point them out and learn from each other. In addition, if you want to know anything, you can also send me an email to discuss with each other, and you will know everything.

Note: Do not reprint without consent!

Guess you like

Origin blog.csdn.net/senllang/article/details/130902269