Linux网络编程之POSIX条件变量-生产者消费者问题

上一篇文章总结了POSIX信号量与互斥锁的内容,接下来学习条件变量的相关知识,主要有以下内容:条件变量、条件变量函数、条件变量使用规范以及使用条件变量解决生产者消费者问题。

1.条件变量

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

(2)再举一个例子:
1)假设有两个线程同时访问一个全局变量n,这个全局变量的初始值为0;
2)这个时候一个线程进入临界区—>对互斥量进行加锁操作—>访问变量n,但是该线程访问n有个前提条件就是n的值必须大于0。所以,该线程就要等待n大于0这个条件…
3)另外一个线程同样进入临界区—>修改n的变量,使得n>0—>通知等待线程—>退出临界区。

但是这里有一个问题,就是当一个线程进入临界区之后,其他线程就进入不了临界区了,那么第二个线程就没法改变n>0这个条件以满足第一个线程的运行条件。f

(3)针对该问题,解决办法就是使用条件变量。

2.条件变量函数

pthread_cond_init:初始化一个条件变量
pthread_cond_destroy:销毁一个条件变量
pthread_cond_wait:在一个条件之上等待
pthread_cond_signal:当条件满足的时候,向等待条件的第一个线程发起通知
pthread_cond_broadcast:向等待的所有线程发起通知

条件变量通常要跟互斥锁一起使用,当等待条件的时候对该互斥锁进行解锁操作,以便其它线程能够进入到临界区。

3.条件变量使用规范

(1)等待条件代码

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

(2)给条件发送信号代码

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

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

使用条件变量改进生产者消费者问题与上一篇文章使用POSIX信号量和互斥锁实现的模式类型。

(1)首先,同样先定义一些全局变量:生产者和消费者个数,以及互斥锁和条件变量等:

pthread_mutex_t g_mutex;
pthread_cond_t g_cond;
int nready = 0;    //表示当前仓库产品个数

(2)然后在main函数中首先初始化互斥锁和条件变量:
pthread_mutex_init(&g_mutex, NULL);
pthread_cond_init(&g_cond, NULL);

(3)接下来创建若干消费者线程和生产者线程并等待这些线程的退出:
//与使用POSIX信号量和互斥锁实现的模型一样

(4)再就是销毁互斥锁与条件变量:
pthread_mutex_destroy(&g_mutex);
pthread_cond_destroy(&g_cond);

(5)然后就是在线程入口函数中,按照条件变量的使用规则来使用条件变量:
//对于消费者线程入口函数consume来说
void *consume(void *arg)
{
    int num = (int)arg;
    while(1)
    {
         pthread_mutex_lock(&g_mutex);//上锁
         while(nread == 0)//条件不满足时,就一直等待…
         {
               pthread_cond_wait(&g_cond, &g_mutex);
         }
         //跳出while循环就说明条件满足了,就可以消费产品了
         –nready;
         pthread_mutex_unlock(&g_mutex);//解锁
    }
    return NULL;
}

//对生产者线程入口函数produce来说类似

while(1)
{
pthread_mutex_lock(&g_mutex);
++nready;
pthread_cond_signal(&g_cond);//发起通知
pthread_mutex_unlock(&g_mutex);
}

综上

输出:

(6)最后我们再来分析一下消费者线程入口函数中的pthead_cond_wait函数做了哪些事情?
pthread_cond_wait(&g_cond, g_mutex);

1)首先对g_mutex进行解锁;
2)等待条件,直到有线程向它发起通知;
3)重新对g_mutex进行加锁。

发表评论

电子邮件地址不会被公开。 必填项已用*标注