读者作家问题

时间:2009-03-25 14:22:58

标签: multithreading language-agnostic producer-consumer

很抱歉,如果我再次提出同样的问题,但想要验证!

我有两个进程P1和P2。

P1是作家(制片人) P2是读者(消费者)。

有一些共享内存或P1写入的文件,一旦P1写入,P2应该被通知读取。

现在根据我的理解,P1的伪代码应该是

Open shared file
Create a named event("Writedone") to signal write for P2 process
Do some processing on file
Mutex.Lock()
Write to File
Mutex.Unlock()
Signal named Event.
CloseHandle on file

现在在P2

Open handle to Shared file
Open handle to named event
WaitForSingleEvent on named event("Writedone")
Read from file
CloseHandle on file

问题:

  1. 是否需要在阅读器中锁定?读者只需读取文件而不进行更改。所以我猜读者不需要锁。思考?在没有锁的某些情况下会出错吗?
  2. 我每次在阅读和写作期间打开和关闭文件的句柄。我认为这不是必需的。我可以在构造函数中打开文件句柄,并在读者和编写器的析构函数中关闭它。但是,当它以书面形式使用时,我可以从文件中读取吗?
  3. 编辑:每次编写器在文件末尾写入10个字节,读者应该读取写入者写的最新10个字节。

5 个答案:

答案 0 :(得分:1)

答案是:如果(且仅当)两个线程可以同时使用相同的共享资源,则必须进行锁定。关于您的具体实施的信息不足,但我的评论很少:

  1. 仅在写作期间锁定是没有意义的。它只会增加一些开销,但在读取器也被正确锁定之前不会阻止任何并发访问。
  2. 如果修改与文件描述符相关的结构的文件操作不以任何方式同步,则需要锁定。当P2仍在读取时,P1可能会开始写入文件。如果读取和写入操作修改相同的系统结构而没有任何底层同步,则最终会导致数据损坏。很难说这是否是这种情况,因为您没有提到您使用的特定功能(库)。文件操作在大多数系统上都是同步的,所以它应该不是问题。
  3. 根据您所写的“10字节信息部分”,显式锁定似乎没有必要(除非#2不强加它)。 P1产生数据量。当数据准备好被读取时,P1通知P2(通过事件;事件传递应该在内部同步,无论如何)。 P2知道它可以读取数据量,然后需要等待后续通知。可能会发生后续通知将在处理前一个通知之前发送。所以,事件需要以某种方式排队。您也可以使用信号量而不是事件通知。

答案 1 :(得分:0)

你需要读者获得锁定 - 事件的使用是无可替代的。没有它,作家可以在读者代码中的任何一点开始写作。

答案 2 :(得分:0)

您绝对需要消费者锁定,以防止生产者在读者阅读之前附加到文件。想象一下这种情况:

Producer writes and signals
Consumer receives signal
Consumer opens the file
Producer fires again and writes another 10 bytes
Producer signals
Consumer reads the last 10 bytes
Consumer closes the file

接下来发生的事情取决于您的命名事件是手动重置还是自动重置。如果它是自动重置,那么消费者将看到第二个信号,然后返回并再次阅读相同的内容。如果它是手动重置,那么消费者将重置事件并错过生产者写的最后一件事。

请注意,即使使用锁定,如果制作人能够快速响应,您也会遇到竞争条件。也就是说,在Consumer能够读取第一条记录之前,Producer可能能够将第二条记录放入文件中。

这里看来你所拥有的是一个在文件中实现的FIFO队列,并且你依赖于消费者处理数据的能力比生产者可以创建它更快。如果你能保证那种行为,那么你没事。否则,消费者必须跟踪它上次读取的位置,以便它知道下一步应该读取的位置。

答案 3 :(得分:0)

  1. 如果作者可以随时开始写,您需要锁定读卡器中的互斥锁。确保互斥锁是一个命名的互斥锁,因此P2可以打开它。

  2. 如果您在两个进程中使用FileShare.ReadWrite打开文件,则可以将其保持打开状态。

  3. 在阅读器中,您可能需要先寻找您点击EOF的地方才能再次阅读。

    如果您确定编写器总是附加并且您可以告诉记录结束的位置(例如,因为它们总是10个字节),并且您可以接受一个小延迟,并且编写器总是写完整记录,您可以执行这根本没有互斥和事件。使用FileShare.ReadWrite打开文件,在阅读器中,继续寻找同一个地方并尝试读取您的记录,如果不能,请暂停一下。如果你设法阅读整个记录,你就得到了一个。弄清楚你的位置并循环回去寻找那个地方并尝试再次阅读。这就是tail -f在Unix中的工作方式。

答案 4 :(得分:0)

除了常规同步功能外,您还可以使用Windows上的file change notification API等待文件更改。

相关问题