POSIX条件变量(生产者,消费者)

本文介绍了POSIX条件变量的概念及其在解决生产者消费者问题中的作用。当线程等待条件变量时,会先解锁互斥锁,允许其他线程修改条件。在条件满足时,通过发送信号通知等待线程。条件变量提供了一种解决无界缓冲区问题的理想方案,简化了生产者和消费者的同步操作。文中详细阐述了条件变量的使用规范,并给出了核心代码示例。
摘要由CSDN通过智能技术生成

POSIX条件变量

1,当一个线程互斥的访问条件变量的时候,它发现这个变量当前的状态不满足这个线程得以继续执行的要求

  就需要等待其它线程对该变量进行更改,直到满足它的要求,不然的话,它什嘛也不做。。。。。。。。。

  等待条件的满足。这个时候呢,,,就需要用到条件变量


  如上,一个全局变量n = 0;

  两个线程,都有加锁机制,一旦进入临界区,当线程1进入临界区之后,那么就会加锁,线程2也就进不去了

  ,然后根本无法使得n大于0,所以线程1也就无法往下继续执行了,出现了死锁

  这个时候,我们就需要其它的手段来解决这个问题了,,,那么就是采用条件变量

  通常一个进程进入临界区会加一个互斥锁(mutex,lock),那么条件变量需要跟互斥锁配合使用

  为什么要配合使用呢???

  1,我们所等待的条件n是多个线程都可以访问的。。。因而对这个条件的保护需要用到锁操作

  2,一旦一个线程对条件进行了互斥锁加锁,那么其它的线程就无法进入到临界区了

    所以说:条件变量能够跟一个锁配合在一起使用,那么条件变量在等待条件的时候,必须能够对这把锁进行

        解锁,,,(也就是说:条件变量在等待的时候,第一步一定是先解锁,如果没有进行解锁,其

        它线程没有机会进入到临界区中,还是跟上面死锁是一样的,一旦解锁了,其它线程就有可能

        进入到临界区使得条件合适(n大于0)),同时一旦等待的线程对互斥锁进行了解锁,也允许

        其它的线程进入到临界区,等待同样的一个条件


  所以说:条件变量是一种新的同步对象,,,当进入到等待条件的时候,首先要进行解锁操作。。。

      这也就是我们为什么需要条件变量的原因,,,


  那么条件变量也可以用于生产者,消费者问题。。。。

  例如:一个线程访问一个队列的时候,发现队列是空的时候,那么它就只能等待,,,直到其它线程将一个

     节点放入到队列中的时候,可以向等待当中的线程发起一个通知。

     这个时候,就用到了条件变量了


  所以说:通过条件变量也能解决生产者,消费者问题,,,而且对于解决缓冲区是无界的。是比较理想的一种

      解决方案(也就是说:生产者这边不管缓冲区是不是满,因为它认为缓冲区是无界的,这个时候,

      我们只需要用一个互斥量和条件变量即可)



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


条件变量的使用规范


等待条件代码:

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 <unistd.h>
#include <sys/types.h>
#include <pthread.h>
#include <semaphore.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>


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

#define CONSUMERS_COUNT 5
#define PRODUCERS_COUNT 5
//两个生产者一个消费者
pthread_mutex_t g_mutex;

//我们需要一个条件变量对象
pthread_cond_t g_cond;

//另外我们还要注意到建立线程(消费者+生产者的个数)
pthread_t g_thread[CONSUMERS_COUNT + PRODUCERS_COUNT];

int nready = 0; //当前缓冲区中的货物个数
//为0,表示在一开始,没有货物

void *consume(void *arg)
{
	int num = (int)arg;
	while(1)
	{
		pthread_mutex_lock(&g_mutex);
		while(nready == 0)
		{
			printf("%d  begin wait a condtion ....\n", num);
			pthread_cond_wait(&g_cond, &g_mutex);
		}
		printf("%d end wait a condition...\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 signal....\n", num);
		pthread_mutex_unlock(&g_mutex);
		sleep(5);
	}
	return NULL;
}

int main(void) 
{

        int i;
	
	pthread_mutex_init(&g_mutex, NULL);
       
	pthread_cond_init(&g_cond, 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_mutex_destroy(&g_mutex);
	//销毁这些锁
	pthread_cond_destroy(&g_cond);
        //销毁这些条件变量
	return 0;
}

运行结果:


我们再来看看细节问题:

生产者消费者的核心代码:

生产者:

while(1)
	{
		pthread_mutex_lock(&g_mutex);
		++nready;
		 pthread_cond_signal(&g_cond);
		 pthread_mutex_unlock(&g_mutex);
                 sleep(1);
       }

消费者:

while(1)
	{
		pthread_mutex_lock(&g_mutex);
		while(nready == 0)
		{
			pthread_cond_wait(&g_cond, &g_mutex);
		}
		--nready;
		pthread_mutex_unlock(&g_mutex);
      slee(1);
   }

1, 首先我们来看看:

  pthread_cond_wait(&g_cond, &g_mutex);
  这个函数内部做了什嘛事情,,,

  1,对g_mutex进行解锁(给其它线程进入临界区的条件,才能够更改条件)

    其它线程也就有机会进入临界区,处于等待状态

    而且,我们在使用这个函数之前,一定要先进行加锁操作。。。。

  2,等待条件,直到有线程向它发起通知,才返回

  3,当函数返回的时候,一定要对这个g_mutex进行加锁操作。。。。


  上面这三者,构成了一个原语


2,再来看看这个函数:


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


3,这个函数

pthread_cond_broadcast

  向所有等待的线程发起通知。。。。。


4,接下来,我们来讨论讨论为什么消费者这里要用while,if不行吗???

  

    while(nready == 0)
		{
			pthread_cond_wait(&g_cond, &g_mutex);
		}

   1,当信号处理程序完毕的时候,,,等待的函数会重新等待一样,,就好像这个信号没有发生过一样,

     这个函数会自动重启

   2,虚假的唤醒,说明我们并没有等待到条件,,,,此时的nready还可能为0,用if达不到要求




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值