本文概述
(生产者消费者问题)
让我们检查一下睡眠和唤醒的基本模型。假设我们有两个系统调用, 分别是sleep和wake。调用睡眠的线程将被阻塞, 而调用线程将被唤醒。
有一个流行的例子称为生产者消费者问题, 它是模拟睡眠和唤醒机制的最流行的问题。
睡眠和唤醒的概念非常简单。如果关键部分不为空, 则该线程将进入休眠状态。当前正在关键部分执行的另一个进程将唤醒它, 以便该进程可以进入关键部分。
在生产者消费者问题中, 我们说有两个线程, 一个线程写东西, 另一个线程读东西。正在编写内容的线程称为生产者, 而正在读取内容的线程称为消费者。
为了进行读取和写入, 它们两个都使用缓冲区。下面显示了在为生产者消费者问题提供解决方案方面模拟睡眠和唤醒机制的代码。
#define N 100 //maximum slots in buffer
#define count=0 //items in the buffer
void producer (void)
{
int item;
while(True)
{
item = produce_item(); //producer produces an item
if(count == N) //if the buffer is full then the producer will sleep
Sleep();
insert_item (item); //the item is inserted into buffer
count=count+1;
if(count==1) //The producer will wake up the
//consumer if there is at least 1 item in the buffer
wake-up(consumer);
}
}
void consumer (void)
{
int item;
while(True)
{
{
if(count == 0) //The consumer will sleep if the buffer is empty.
sleep();
item = remove_item();
count = count - 1;
if(count == N-1) //if there is at least one slot available in the buffer
//then the consumer will wake up producer
wake-up(producer);
consume_item(item); //the item is read by consumer.
}
}
}
生产者生产该项目并将其插入缓冲区。每次插入时, 全局变量计数的值都会增加。如果缓冲区已满, 并且没有可用的插槽, 则生产者将进入睡眠状态, 否则它将继续插入。
在消费者方面, 每次消费时count的值都会减少1。如果缓冲区在任何时间点都是空的, 那么消费者将以其他方式入睡, 它将继续消耗这些物品并将count的值减少1。
如果缓冲区中至少有1个要消费的项目可用, 生产者将唤醒消费者。如果缓冲区中至少有一个可用插槽, 则生产者将被消费者唤醒, 以便生产者可以写入该插槽。
嗯, 问题发生在消费者即将要睡觉之前被抢占的情况下。现在, 消费者既不睡觉也不消费。由于生产者并不了解消费者实际上并未睡觉的事实, 因此它会继续唤醒消费者, 而消费者由于未睡觉而没有响应。
这导致浪费系统调用。当消费者重新安排时间时, 它将进入睡眠状态, 因为它在被抢占时即将进入睡眠状态。
生产者继续在缓冲区中写内容, 过了一段时间它就写满了。生产者也将在那个时候睡觉, 记住当缓冲区中有可用插槽时, 消费者将唤醒他。
消费者还在睡觉, 并不知道生产者会叫醒他。
这是一种僵局, 生产者和消费者都不活跃, 彼此等待对方将其唤醒。这是一个严重的问题, 需要解决。
使用标志位摆脱这个问题
可以使用标志位来解决此问题。生产者可以在第一次调用唤醒时设置该位。消费者安排好时间后, 便检查该位。
消费者现在将知道生产者试图唤醒他, 因此它将不会进入睡眠状态, 可以消费生产者生产的任何东西。
该解决方案仅适用于一对生产者和消费者, 如果有n个生产者和n个消费者, 该怎么办。在那种情况下, 需要保持一个整数, 该整数可以记录已经进行了多少次唤醒呼叫以及有多少消费者不需要睡眠。此整数变量称为信号量。稍后我们将详细讨论有关信号量的更多信息。
评论前必须登录!
注册