进程与线程之生产者和消费者问题

    在学习进程和线程的过程中,毫无疑问肯定会学到多线程、进程间通信等相关问题。而这也是学习进程和多线程方面的一个重点。这篇文章主要介绍的是利用互斥量、锁以及使用Pthread库来实现生产者和消费者问题。

1、临界区

    临界区指的是共享内存进行访问的程序片段。在实现线程间同步就必须只有一个线程访问临界区。


上图中进程A在T1时刻进入临界区,当运行到T2时刻的时候进程B试图进入临界区。因为此时进程A已经在临界区,所以此时进程B被阻塞,当T3时刻A离开临界区进程B便进入临界区。运行到T4时刻B便离开临界区。

2、互斥量

    互斥量是一个只存在两个状态的变量:解锁和加锁,如果用一个整型变量表示的话就是0和1。当某个线程要进入临界区时,先检查互斥量是否在上锁状态,如果不是,则将互斥量更改为上锁状态,同时进入该临界区。当线程运行完毕需要退出临界区,此时将互斥量更改为解锁状态,如果有下一个需要进入临界区的线程将得到该互斥量,并更改其状态。

    在Pthread库中,使用pthread_mutex_lock来获得一个锁。如果多个线程在等待同一个互斥量,当它被解锁时,这些等待的线程中只有一个被允许运行并将互斥量重新锁定。这些互斥锁不是强制性的,而是由程序员来保证线程正确地使用它们。

线程调用描述
pthread_mutex_init创建一个互斥量
pthread_mutex_destory    撤销一个已存在的互斥量
pthread_mutex_lock获得一个锁或阻塞
pthread_mutex_trylock获得一个锁或失败
pthread_mutex_unlock释放一个锁

3、条件变量

    互斥量在允许或阻塞状态对临界区的访问上是很有用的,条件变量则允许线程由于一些未达到的条件而阻塞。绝大部分情况下这两种方法是一起使用的。等待条件变量的线程通常处于阻塞状态,它会等待发信号的线程去做某些工作、释放掉某些资源或是进行其他的一些活动。

    条件变量与互斥量经常一起使用。这种模式用于让一个线程锁住一个互斥量,然后当它不能获得它期待的结果时等待一个条件变量。最后另一个线程会向它发信号,使它可以继续运行。pthread_cond_wait原子性地调用并解锁它持有的互斥量。

线程调用描述
pthread_cond_init创建一个条件变量
pthread_cond_destroy撤销一个条件变量
pthread_cond_wait阻塞以等待一个信号
pthread_cond_signal向另一个线程发信号来唤醒它
pthread_cond_broadcast向多个线程发信号来让它们全部唤醒

4、生产者、消费者问题

    一个线程将产品放在一个缓冲区里,由另一个线程将它们取出。如果生产者发现缓冲区是满的(没有被消费者及时取走),那么生产者会处在阻塞状态,直到消费者将缓冲区的内容取走同时并唤醒生产者线程将缓冲区填满。当消费者发现缓冲区是空时,消费者线程被阻塞,同时并唤醒生产者将缓冲区填满。一旦缓冲区被填满,消费者线程也会被唤醒以取走缓冲区的内容。

    

#include "stdafx.h"
#include "pthread.h"
pthread_mutex_t the_mutex;//声明一个全局变量的互斥量
const int MAX=20;        //需要生产的最大数目
pthread_cond_t condc,condp;   //生产者和消费者的条件变量
int buffer=0;            //缓冲区
void *producer(void *ptr)
{
	for (int i=0;i<MAX;i++)
	{
		pthread_mutex_lock(&the_mutex);   //获得互斥量并上锁
		while (buffer!=0)        //当缓冲区的是满的(在这里buffer大于0表示缓冲区是满的,等于0表示缓冲区是空的)
		{
			pthread_cond_wait(&condp,&the_mutex);  //当缓冲区是满的时候,生产者线程会处于阻塞状态
		}
		buffer=i;   //当缓冲区是空的就将缓冲区填满
		pthread_cond_signal(&condc);  //填满缓冲区之后唤醒消费者线程
		pthread_mutex_unlock(&the_mutex); //释放互斥锁
	}
	pthread_exit(0);  //退出消费者线程
	return NULL;
}

void *consumer(void *ptr)
{
	for (int i=0;i<MAX;i++)
	{
		pthread_mutex_lock(&the_mutex);//获得互斥锁
		while (buffer==0)    //当缓冲区为空时,消费者进程就一直阻塞
		{
			pthread_cond_wait(&condc,&the_mutex);
		}
		buffer=0;    //当缓冲区被填满后,消费者线程就将缓冲区的内容取出来
		pthread_cond_signal(&condp);    //取出内容后将生产者线程唤醒
		pthread_mutex_unlock(&the_mutex);    //释放互斥锁
	}
	pthread_exit(0);    //退出消费者线程
	return NULL;
}
int _tmain(int argc, _TCHAR* argv[])
{
	pthread_t conc,conp; //声明生产者和消费者的条件变量
	pthread_mutex_init(&the_mutex,0); //初始化互斥量
	pthread_cond_init(&condc,NULL);//初始化两个条件变量
	pthread_cond_init(&condp,NULL);
	pthread_create(&conp,NULL,producer,NULL); //创建生产者线程
	pthread_create(&conc,NULL,consumer,NULL); //创建消费者线程
	pthread_join(conp,0);    
	pthread_join(conc,0);
	pthread_cond_destroy(&condp); //撤销生产者和消费者的条件变量
	pthread_cond_destroy(&condc);
	pthread_mutex_destroy(&the_mutex);//撤销互斥量
	return 0;
}
    给改程序设置断点运行,可以观察到:当buffer==0的时候消费者的线程会被阻塞,同时跳到生产者线程来执行,将缓冲区非填满。而如果buffer!=0时,这时生产者的线程会被阻塞而跳到消费者线程来执行并将缓冲区的内容取出来。
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值