对于单缓冲区,不存在生产者和消费者同时访问缓冲区的情况。而对于多个缓冲区情况会稍微复杂些,有可能存在同时访问缓冲区的情况,这是不被允许的。需要采取措施防止此种操作。可以采用信号量也可以使用互斥量。
Item B[k]; Semaphore empty=k; Semaphore full=0; Semaphore mutex=1; Int i=0; Int out=0; Void producer() { Produce(); P(mutex); P(empty); Append to Item[in]; In++=%k; V(full); V(mutex); } Void consumer() { P(mutex); P(full); Take from Item[out]; Out++=%k; V(empty); V(mutex); } |
上述是针对多缓冲区的操作,P(muex)和V(mutex)必须成对出现,它们之间的就是进程临界区。上述代码存在问题:当缓冲区存满k件产品时,此时empty=0,mutex=1,full=k;此后生产者又生产了一件产品,当它向缓冲区存放产品时将在P(empty)上等待,此时empty=-1,由于此时mutex=0,已经占有了缓冲区,消费者会在p(full)上等待。这就导致了互相等待的情况。所有在使用信号量和PV操作实现进程同步时,特别要当心P操作的顺序,而V操作的顺序无关紧要。无论何时应先判断同步信号,再判断互斥信号。下面是改正后的代码。
Void producer() { Produce(); P(empty); P(mutex); Append to Item[in]; In++=%k; V(mutex); V(full); } Void consumer() { P(full); P(mutex); Take from Item[out]; Out++=%k; V(mutex); V(empty); } |
读者和写者也是一个经典的并发程序设计问题。有两组并发线程:读者和写者,它们共享文件F。要求:
1:允许多个读者读取文件。
2:只允许一个写者执行写操作。
3:任何写者工作前,必须等待所有读者释放对文件的战友。
4:写者工作时不允许其他读者或写者工作。