第四十章 POSIX条件变量


条件变量

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

条件变量和互斥锁为什么要配合使用?

  1. 条件本身就是公共资源,多个线程同时方式时,必须使用互斥锁在临界区内对条件进行保护。
  2. 条件变量的作用是在等待某个条件达成时自身要进行睡眠或阻塞,避免忙等待带来的不必要消耗;当被唤醒时,会重新尝试加锁,如果锁成功,才进行之后的流程;否则解锁,继续阻塞

条件变量函数

pthread_cond_init

功能:
    initialize condition variables
原型:
    int pthread_cond_init(pthread_cond_t *restrict cond,
           const pthread_condattr_t *restrict attr);
    pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
参数:
    cond : 指向结构pthread_cond_t的指针
    attr : 条件变量的属性结构的指针
返回值:
    成功 : 0
    失败 : 返回错误码

pthread_cond_destroy

功能:
    destroy condition variables
原型:
    int pthread_cond_destroy(pthread_cond_t *cond);
参数:
    cond : 指向结构pthread_cond_t的指针
返回值:
    成功 : 0
    失败 : 返回错误码

pthread_cond_wait

功能:
    wait on a condition
原型:
    int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
参数:
    cond : 指向结构pthread_cond_t的指针
    mutex : 互斥锁
返回值:
    成功 : 0
    失败 : 返回错误码

pthread_cond_signal

功能:
    signal a condition
原型:
    int pthread_cond_signal(pthread_cond_t *cond);
参数:
    cond : 指向结构pthread_cond_t的指针
返回值:
    成功 : 0
    失败 : 返回错误码

pthread_cond_broadcast

功能:
    signal a condition
原型:
    broadcast a condition
参数:
    cond : 指向结构pthread_cond_t的指针
返回值:
    成功 : 0
    失败 : 返回错误码

条件变量使用规范

等待条件代码

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

给条件发送信号代码

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

使用条件变量解决生产者、消费者问题

#include 
#include 
#include 

#include 
#include 
#include 
#include 

#include 

#define ERR_EXIT(m)         \
    do                      \
    {                       \
        perror(m);          \
        exit(EXIT_FAILURE); \
    } while (0)

#define CONSUMERS_COUNT 2 //消费者线程的个数
#define PRODUCERS_COUNT 1 //生产者线程的个数


int nready = 0;
pthread_cond_t g_cond;
pthread_mutex_t g_mutex;

pthread_t g_thread[CONSUMERS_COUNT + PRODUCERS_COUNT]; //总线程数

void *consume(void *arg)
{
    int num = (int)arg;
    while (1)
    {
        pthread_mutex_lock(&g_mutex);
        while(nready == 0)
        {
            printf("%d wait a condition...\n", num);
            pthread_cond_wait(&g_cond, &g_mutex);
        }
        printf("%d end wait...\n", num);
        printf("%d begin consume product...\n",num);
        --nready;
        printf("%d end consume product...\n",num);
        pthread_mutex_unlock(&g_mutex);
        sleep(1);
    }

    return NULL;
}

void *produce(void *arg)
{
    int num = (int)arg;
    while (1)
    {
        pthread_mutex_lock(&g_mutex);
        printf("%d begin produce product...\n", num);
        ++nready;
        printf("%d end produce product...\n", num);
        pthread_cond_signal(&g_cond);
        printf("%d singal ...\n",num);
        pthread_mutex_unlock(&g_mutex);
        sleep(1);
    }

    return NULL;
}

int main()
{
    int i;
    pthread_cond_init(&g_cond, NULL);
    pthread_mutex_init(&g_mutex, NULL);

    for (i = 0; i < CONSUMERS_COUNT; ++i)
    {
        pthread_create(&g_thread[i], NULL, consume, (void *)i);
    }

    sleep(1);

    for (i = 0; i < PRODUCERS_COUNT; ++i)
    {
        pthread_create(&g_thread[CONSUMERS_COUNT + i], NULL, produce, (void *)i);
    }

    for (i = 0; i < CONSUMERS_COUNT + PRODUCERS_COUNT; ++i)
    {
        pthread_join(g_thread[i], NULL);
    }

    pthread_cond_destroy(&g_cond);
    pthread_mutex_destroy(&g_mutex);

    return 0;
}

pthread_cond_wait

  1. 对mutex进行解锁
  2. 等待条件,直到有线程向它发起通知
  3. 重新对mutex进行加锁操作

pthread_cond_signal
向等待的条件的线程发起通知,如果没有任何一个处于等待条件的状态,这个通知将被忽略

为什么要用while

  1. pthread_cond_wait会自动重启,就像这个信号没有发生过一样
  2. pthread_cond_wait可能会被虚假的唤醒,当被虚假唤醒后,需要重新判断条件

相关