【问题标题】:Producer / Consumers problem question生产者/消费者问题问题
【发布时间】:2010-12-04 18:16:04
【问题描述】:

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

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

writeSemaphore,初始化为 5,是告知是否允许写入者写入缓冲区的信号量。当它最终下降到 0 时,父进程将被阻塞,直到其中一个子进程解锁它(在读取了一些块之后)。

readSemaphore,初始化为 0,是指示是否允许任何读取器从缓冲区读取的信号量。

但是现在我想起来了,这不会阻止我让 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)
}

我的推理正确吗?

谢谢

【问题讨论】:

标签: multithreading unix fork semaphore shared-memory


【解决方案1】:

Khachik 说对了一半。他可能还好,但他的描述不够清楚。

首先,如果您让父级发布allowedToRead,您可能意味着它发布readSemaphore

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

第三,对于allowedToRead,我会使用互斥体而不是信号量。

第四,是什么决定了哪个孩子读取了哪些数据,或者它意味着先到先得,就像污水桶里的猪一样?

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

【讨论】:

    【解决方案2】:

    没有。

    1. 作者在写一个单元信息时应该释放readSemaphore
    2. 写入者在写入共享内存之前应获取allowedToRead 锁(0,1 信号量是锁/互斥锁)以防止出现竞争条件。

    为了简化:考虑两个函数read_shared_memorywrite_shared_memory,分别是从共享内存读取和写入,并且在读取/写入之前都获取/释放相同的锁。

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

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

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

    【讨论】:

    • 我还是不明白我的实现有什么问题。我能得到什么比赛条件?当生产者正在写入时,消费者正在等待。当消费者阅读时,一次只能阅读其中一个。
    • 它只抱怨几个生产者的竞争条件——我只有 1 个。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-27
    • 2011-08-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多