在多线程编程过程中,为了保证是原子操作,必须处理好线程之间的同步和互斥,生产者消费者问题即是线程间同步和互斥的经典例子。
生产者消费者问题的描述:
生产者负责生产“产品”,消费者负责消费“产品”,当生产的产品满时(假设有一个生产的流水线),需等待消费者消费产品后才可以生产产品,不然就会溢出,当流水线上没有产品时,消费者就等待生产者生产产品。
问题分解如下:
(1)一个生产者对应一个生产者线程,多个生产者就对应多个生产者线程,因此需注意多个生产者(也就是多个线程)之间的互斥(用互斥锁);
(2)消费者问题同(1);
(3)生产者和消费者需处理好线程同步问题,因为生产者在缓冲区为满时需等待,在消费者为缓冲区为空时需等待,生产者在生产“产品”后需告诉消费者,消费者在消费“产品”后需告诉生产者(使用信号量或者是互斥锁+条件变量)。
实现思路如下:
第一种方法:
采用信号量,信号量是常见的一种线程同步方式,它的特点是它是一个非负整数,当获得(P)某一个信号量时,该信号量的值减去1,当释放(V)一个信号量时,该信号量的值加1;当信号量的值为0时,所有试图获得(P)该信号量的操作都将阻塞等待,直到该信号量值大于0。
在生产者消费者问题中,设置两个信号量:S1--表示缓冲区空的个数 S2--表示缓冲区满的个数,很显然,初始状态下:S1 = n ,S2 = 0;另外还需设置一个互斥锁m,用来多个线程互斥。
伪代码如下:
producer
{
//其他操作....
P(S1) //当某一个生产者获取时就会减一,当减到0时会等待
P(m) //互斥操作
//生产者开始生产数据,即往缓冲区送数据
V(m)
V(S2) //当生产者生产数据后,S2加1
}
consumer
{
P(S2)
P(m)
//消费者消费数据,即从缓冲区取数据
V(m)
V(S1)//消费者消费数据后,相应的S1加1
}
第二种方法:互斥锁(m)+条件变量(not_full和not_empty)
伪代码如下:
producer
{
P(m)
while(缓冲区为满)
{
not_full.wait() //当为满时,等待,进入休眠
}
生产者生产数据,往缓冲区放数据
V(m)
not_empty.notify() //通知消费者缓冲区不空
}
consumer
{
P(m)
while(缓冲区为空)
{
not_empty.wait();//当无产品时,消费者等待不空
}
...... //消费者消费产品,从缓冲区中取数据
V(m)
not_full.notify();//通知生产者不满了。
}
其实第二种方式封装一下就是实现了有边界的阻塞队列,以后总结。