生产者/消费者问题

时间:2010-12-04 18:16:04

标签: multithreading unix fork semaphore shared-memory

我将实现一个程序,其中一个父进程读取一个文本文件,并将他正在读取的数据提供给一些子进程将读取的共享内存缓冲区。所有这些工作都将由信号量调解。假设父节点将从文件中一次读取一个字符,共享内存缓冲区包含5个插槽。

起初,我认为只有2个信号量:

初始化为5的

writeSemaphore是信号量,它告诉写入者是否被允许写入缓冲区。当它最终变为0时,父进程将被阻塞,直到其中一个子进程解锁(在读完一些块之后)。

初始化为0的

readSemaphore是信号量,它告诉是否允许任何读者从缓冲区读取。

但是现在我想起来了,这不会阻止我让2个消费者同时访问共享内存。我必须阻止它。所以我介绍了第三个信号量:

allowedToRead,它是1或0,允许或阻止访问子进程。

以下是儿童和父母的伪代码:

子:

while (something) {
    wait(readSemaphore)
    wait(allowedToRead)
    <<read from shared memory>>
    post(allowedToRead)
    post(writeSemaphore)
}

父:

while (something) {
    wait(writeSemaphore)
    <<writes to shared memory>>
    post(allowedToRead)
}

我的推理是否正确?

由于

2 个答案:

答案 0 :(得分:1)

没有

  1. 作者在写一个单位信息时应该发布readSemaphore;
  2. 作者应该在写入共享内存之前获取allowedToRead锁(0,1信号量是锁/互斥锁)以防止竞争条件。
  3. 简化:考虑两个函数read_shared_memorywrite_shared_memory,它们分别从共享内存中读取和写入,并在读取/写入之前获取/释放相同的锁。

    生产者获取写信号量,调用write函数,释放读取的信号量。 消费者获取读取信号量,调用读取函数,释放写入信号量。

    当然,这可以在没有读/写功能的情况下实现,它们只是为了简化对共享内存的原子访问。关键部分可以在生产/消费循环中实现而无需额外的功能。

    Wikipedia以更科学的方式描述它:)

答案 1 :(得分:1)

Khachik是对的。他可能没事,但他的描述并不尽如人意。

首先,如果您有父发布allowedToRead,则可能需要发布readSemaphore

其次,您的代码允许父母在孩子阅读的同时写作。你说你有5个插槽。如果父母写入不同于孩子正在阅读的插槽,那么我认为这是可以的,但孩子如何确定在何处阅读?它是否使用与父进程相同的变量来确定写入的位置?你可能需要一些额外的保护。毕竟我认为不同的孩子都在阅读不同的插槽,所以如果你需要防止他们踩到其他人的脚趾,你也需要为父母做同样的事情。

第三,我使用了互斥锁而不是allowedToRead的信号量。

第四,是什么决定哪个孩子读取哪个数据或者它首先应该像猪一样在垃圾桶中服务?

如果共享内存有5个独立的插槽,那么我倾向于添加“下一个读取”和“下一个写入”变量。在生产者和消费者中使用互斥锁保护这两个变量,然后使用信号量来阻止/触发读取和写入,就像您已经在做的那样。如果它不是学校练习,你可以使用附加到我提到的互斥锁的单个条件变量做得更好。当它被发出信号时,父母检查他是否可以写,孩子们检查他们是否可以阅读。当发生读取或写入时,全局发出条件变量信号以唤醒所有人以检查其状况。这样做的好处是,如果你有独立的缓冲区插槽,那么你可以安全而愉快地让多个消费者同时消费。