正文:
信号量没有 “触及上限则阻塞post” 的原语,同时信号量除了系统限制的信号量最大值之外并没有接口可以用来设置上限。因此在一个信号量场景下,生产者在 post 信号的时候是没有束缚的,如果不控制生产量的话,会导致系统资源被耗尽。
一种方法是判断 FIFO 的尺寸,如果 FIFO 已经满了,则停止本次生产,接着sleep一定时间等待消费者从队列中取走数据,然后判断队列是否为空或者是否降到一定阈值,如果满足则继续填充队列。这种方法有一个问题,那就是如果消费者突然间在短时间内把数据都取走了,那么生产者sleep的就是影响了效率,如果缩减sleep的周期,那么又会导致cpu升高。
另外一种方法是使用两个信号量,此时不再使用一个信号量来管理整个队列的计数,而是预先把队列的上限确定下来,然后用两个信号量分别表示队列中空余(empty)位置的数量 和 已被使用(filled/full)位置的数量,这两个值的和是队列的上限。
生产者的逻辑为 :
1)wait 是否有empty位置可用(sem_wait(empty_sem)) 。
2)一旦跳出阻塞则说明有被标记为 empty 的位置可用,即有未被填充的单元,那么 lock 队列(mutex_lock/sem_wait(二元sem),这里可以选择mutex,也可以用二元sem,mutex 也同时支持 线程和进程级别。
3)填充队列。
4)unlock 队列。
5)post 增加一个 filled/full 可用位置(sem_post(filled_sem))。
消费者逻辑为:
1)wait 是否有 filled 位置可用(sem_wait(filled_sem))。
2) 一旦跳出阻塞则说明有标记为的 filled 的位置可用,即有已经被填充的单元,那么 lock 队列(mutex_lock/sem_wait(二元sem),这里可以选择mutex,也可以用二元sem,mutex 也同时支持 线程和进程级别。
3)从队列中取数据。
4)unlock 队列
5)post 增加一个 empty 可用位置(sem_post(empty_sem))。
伪代码:
mutex
semFull = 0 // Initially, all slots are empty. Thus full slots are 0
semEmpty = n // All slots are empty initially
//Solution for Producer –
do{
//produce an item
wait(semEmpty); //semEmpty - 1
wait(mutex); //mutex.lock
//place in buffer
signal(mutex); //mutex.unlock
signal(semFull ); //semFull + 1
}while(true)
//Solution for Consumer –
do{
wait(full); //semFull - 1
wait(mutex); //mutex.lock
// consume item from buffer
signal(mutex); //mutex.unlock
signal(empty); //semEmpty + 1
}while(true)