线程中的条件变量

  • Post author:
  • Post category:其他


当一个线程互斥的访问某个变量时,它可能发现在其他线程改变状态之前,它什么也做不了。例如,一个线程访问队列时,发现队列为空,它只能等待,直到其它线程将一个节点添加到队列中,这种情况就需要用到条件变量。

一、条件变量函数

(1)初始化

int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condatrr_t *restrict attr);

参数:

cond:要初始化的条件变量;

attr:NULL。

(2)销毁

int pthread_cond_destroy(pthread_cond_t *cond);

(3)等待条件满足

int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);

参数:

cond:要在这个条件变量上等待;

mutex:互斥量,后面详细解释。

(4)唤醒等待

int pthread_cond_broadcast(pthread_cond_t *cond);

int pthread_cond_signal(pthread_cond_t *cond);

【例】

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<pthread.h>

pthread_cond_t cond;
pthread_mutex_t mutex;

void *route1(void *arg)
{
    while(1){
        pthread_cond_wait(&cond, &mutex);
        printf("action\n");
    }
}

void *route2(void *arg)
{
    while(1){
        pthread_cond_signal(&cond);
        sleep(1);
    }
}

int main(void)
{
    pthread_t tid1, tid2;

    pthread_cond_init(&cond, NULL);
    pthread_mutex_init(&mutex, NULL);

    pthread_create(&tid1, NULL, route1, NULL);
    pthread_create(&tid2, NULL, route2, NULL);

    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);

    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);
}

运行结果:

这里写图片描述

二、 为什么pthread_cond_wait需要互斥量?

1)条件等待是线程间同步的一种手段,如果只有一个线程,条件不满足,一直等下去都不会满足,所以必须要有一个线程通过某些操作改变共享变量,使原先不满足的条件变得满足,并且友好的通知等待在条件变量上的线程。

2)条件不会无缘无故的突然变得满足了,必然会牵扯到共享数据的变化。所以一定要用互斥锁来保护。没有互斥锁就无法安全的获取和修改共享数据。

【例】错误的设计——先上锁,发现条件不满足,解锁,然后等待在条件变量上

pthread_mutex_lock(&mutex);

while(condition_is_false){

pthread_mutex_unlock(&mutex);

//解锁之后,等待之前,条件可能已经满足,信号已经发出,但是该信号可能被错过

pthread_cond_wait(&cond);

pthread_mutex_lock(&mutex);

}

pthread_mutex_unlock(&mutex);

1)由于解锁和等待不是原子操作。调用解锁之后,pthread_cond_wait之前,如果已经有其他线程获取到互斥量,摒弃条件满足,那么pthread_cond_wait将错过这个信号,可能会导致线程阻塞在这个pthread_cond_wait。所以解锁和等待必须是一个原子操作。

2)int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);进入该函数后,会去看条件量等于0不?等于,就把互斥量变成1,直到cond_wait返回后,把条件量改成1,把互斥量恢复成原样。

条件变量使用规范

1)等待条件代码

pthread_mutex_lock(&mutex);
while(条件为假)
    pthread_cond_wait(&cond);
修改条件
pthread_mutex_unlock(&mutex);

2)给条件发送信号代码

pthread_mutex_lock(&mutex);
设置条件为真
pthread_cond_signal(cond);
pthread_mutex_unlock(&mutex);

【例】生产者和消费者模型

图示如下:

这里写图片描述

代码如下

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<pthread.h>

#define CONSUMERS_COUNT 2
#define PRODUCERS_COUNT 2

struct msg{
    struct msg *next;
    int num;
};

struct msg *head = NULL;

pthread_cond_t cond;
pthread_mutex_t mutex;
pthread_t threads[CONSUMERS_COUNT+PRODUCERS_COUNT];

void *consumer(void *p)
{
    int num = *(int *)p;
    free(p);
    struct msg *mp;
    for( ; ; ){
        pthread_mutex_lock(&mutex);
        while(head == NULL){
            printf("%d end wait a condition...\n", num);
            pthread_cond_wait(&cond, &mutex);
        }
        printf("%d end wait a condition...\n", num);
        printf("%d begin consumer product...\n", num);
        mp = head;
        head = mp -> next;
        pthread_mutex_unlock(&mutex);
        printf("Consume %d\n", mp->num);
        free(mp);
        printf("%d end consume product...\n", num);
        sleep(rand()%5);
    }
}

void *producer(void *p)
{
    struct msg *mp;
    int num = *(int *)p;
    free(p);
    for( ; ; ){
        printf("%d begin produce product...\n", num);
        mp = (struct msg*)malloc(sizeof(struct msg));
        mp ->num = rand()%1000 + 1;
        printf("produce %d\n", mp->num);
        pthread_mutex_lock(&mutex);
        mp -> next = head;
        head = mp;
        printf("%d end produce product...\n", num);
        pthread_cond_signal(&cond);
        pthread_mutex_unlock(&mutex);
        sleep(rand()%5);
    }
}

int main()
{
    srand(time(NULL));

    pthread_cond_init(&cond, NULL);
    pthread_mutex_init(&mutex, NULL);

    int i;
    for(i=0; i<CONSUMERS_COUNT; i++)
    {
        int *p = (int *)malloc(sizeof(int));
        *p = i;
        pthread_create(&threads[i], NULL, consumer, (void*)p);
    }

    for(i=0; i<PRODUCERS_COUNT; i++)
    {
        int *p = (int*)malloc(sizeof(int));
        *p = i;
        pthread_create(&threads[CONSUMERS_COUNT+i], NULL, producer, (void*)p);
    }

    for(i = 0; i<CONSUMERS_COUNT; i++)
        pthread_join(threads[i], NULL);

    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);
}

运行结果为:

这里写图片描述



版权声明:本文为m0_38121874原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。