Windows 中的两个进程单个生产者/单个消费者。互斥量、事件或信号量哪个更好
Posted
技术标签:
【中文标题】Windows 中的两个进程单个生产者/单个消费者。互斥量、事件或信号量哪个更好【英文标题】:Two processes single producer / single consumer in Windows. What is better Mutex, Event or Semaphore 【发布时间】:2012-11-21 07:44:45 【问题描述】:我可以使用任一原语使其工作,但我想从性能的角度来看,哪种原语更适合这种情况。
我只需要同步两个进程。总是有两个,不多也不少。一个写入内存映射文件,而另一个以生产者/消费者的方式从中读取。我关心性能,考虑到这个场景有多简单,我想我可以使用一些轻量级的东西,但我不确定哪一个更快,但仍然适合这种场景。
【问题讨论】:
【参考方案1】:第一点:它们都是内核对象,所以它们都涉及从用户模式到内核模式的切换。这本身就产生了足够的开销,以至于您不太可能注意到它们之间在速度或类似方面的任何真正差异。因此,哪个更可取将在很大程度上取决于您如何在共享内存区域中构建数据,以及您如何使用它。
让我们从可能是最简单的情况开始:共享内存区域形成了瓶颈。消费者不阅读的所有时间,生产者都在写,反之亦然。至少在最初,这似乎是我们可以使用单个互斥锁的情况。生产者等待互斥体,写入数据,释放互斥体。消费者等待互斥体,读取数据,释放互斥体。这种情况一直持续到一切都完成为止。
不幸的是,虽然这可以防止生产者和消费者同时使用共享区域,但它确实不确保正常运行。例如:生产者写入一个充满信息的缓冲区,然后释放互斥体。然后它再次等待互斥体,所以当阅读器完成后,它可以写入更多数据——但在那时,不能保证消费者将是下一个获得互斥体的人。生产者可能会立即将其取回,并在其刚刚生成的内容之上写入更多数据,因此消费者将永远不会看到之前的数据。
防止这种情况的一种方法是使用几个事件:一个从生产者到消费者说有数据等待读取,另一个从消费者到生产者说所有数据在缓冲区已被读取。在这种情况下,生产者等待其事件,消费者只有在完成读取数据时才会设置该事件。生产者然后写入一些数据,并通知消费者的事件说一些数据已准备好。消费者读取数据,然后向生产者发送事件信号,以便循环继续。
只要您只有一个生产者和一个消费者并且将整个数据视为一起控制的单个“块”数据,这就足够了。然而,这可能会导致问题。例如,让我们考虑一个 Web 服务器前端作为生产者,后端作为消费者(以及一些将结果传递回 Web 服务器的单独机制)。如果缓冲区小到只能容纳一个请求,则生产者可能必须在消费者处理一个请求时缓冲几个传入的请求。每次消费者准备好处理请求时,生产者必须停止正在执行的操作,将请求复制到缓冲区,并让消费者知道它可以继续。
然而,独立进程的基本点是让每个进程尽可能按照自己的时间表进行。为此,我们可能会在共享缓冲区中为许多请求腾出空间。在任何给定时间,这些插槽中的某些数量将被填满(或者,从另一个方向看,某些数量将是空闲的)。对于这种情况,我们只需要一个计数信号量来跟踪这些插槽。只要至少有一个插槽空闲,生产者就可以随时写一些东西。消费者可以在至少一个插槽被填满的任何时候阅读。
底线:选择与速度无关。这是关于您如何使用/构建数据以及流程对数据的访问。假设它真的像你描述的那么简单,这对事件可能是最简单的机制。
【讨论】:
感谢您的彻底回复。我绝对首先关心正确性然后是性能。瓶颈不在共享内存区域。一个进程从用户那里得到一个命令,并确保另一个进程得到相同的命令。这种情况大约每秒发生 100 次。实际上需要生产者停止直到消费者消费数据。对于类似的问题,但在多线程中,我喜欢使用 Criticalsection/Conditionvariable 对象。看来按照你说的,我这里可以用类似的方式使用Event对象。那是对的吗?谢谢! @cloudraven:就您所说的而言,听起来事件应该可以工作。 OTOH,如果涉及的数据量很小,因此您主要需要最小延迟,那么通过管道处理它可能会容易得多(这几乎只是一个小型共享内存缓冲区,但在内核内部进行管理以最大程度地减少难度和开销)。 但当然......对于这种特殊情况,使用命名管道更有意义。完全忘记了他们。谢谢以上是关于Windows 中的两个进程单个生产者/单个消费者。互斥量、事件或信号量哪个更好的主要内容,如果未能解决你的问题,请参考以下文章
Linux相互排斥与同步应用:posix线程实现单个生产者和单个消费者模型