Concurrency 何时使用中断器模式,何时使用本地存储和工作窃取?

Concurrency 何时使用中断器模式,何时使用本地存储和工作窃取?,concurrency,concurrent-programming,disruptor-pattern,work-stealing,Concurrency,Concurrent Programming,Disruptor Pattern,Work Stealing,以下是正确的吗 如果必须以多种方式(io操作或注释)处理每个条目,则具有更好的并行性能和可伸缩性,因为可以使用多个使用者并行处理,而不会产生争用 相反,(即本地存储条目并从其他线程窃取条目)如果每个条目只能以单一方式处理,则具有更好的并行性能和可伸缩性,因为在中断器模式中将条目分散到多个线程上会导致争用 (当涉及多个生产者时,破坏者模式是否比其他无锁多生产者多消费者队列(例如)快得多?) 我的详细情况: 处理一个条目会产生几个新条目,这些条目最终也必须被处理。性能具有最高优先级,按FIFO

以下是正确的吗

  • 如果必须以多种方式(io操作或注释)处理每个条目,则具有更好的并行性能和可伸缩性,因为可以使用多个使用者并行处理,而不会产生争用
  • 相反,(即本地存储条目并从其他线程窃取条目)如果每个条目只能以单一方式处理,则具有更好的并行性能和可伸缩性,因为在中断器模式中将条目分散到多个线程上会导致争用
(当涉及多个生产者时,破坏者模式是否比其他无锁多生产者多消费者队列(例如)快得多?)


我的详细情况

处理一个条目会产生几个新条目,这些条目最终也必须被处理。性能具有最高优先级,按FIFO顺序处理的条目具有第二优先级

在当前的实现中,每个线程使用一个本地FIFO,并在其中添加新的条目。空闲线程从其他线程的本地FIFO窃取工作。线程处理之间的依赖关系使用无锁哈希表(CASs on write,具有桶粒度)来解决。这会导致相当低的争用,但FIFO顺序有时会被打破

使用中断器模式将保证FIFO顺序。但是,将条目分发到线程上是否会导致比本地FIFO更高的争用(例如,读取游标上的CAS)以及工作窃取(每个线程的吞吐量大致相同)


我找到的参考资料

(第5+6章)中的性能测试不包括不相交的工作分配

是我找到的唯一关于干扰者+工作盗窃的参考资料。它指出,如果存在任何共享状态,则每个线程的队列速度会显著降低,但没有详细说明或解释原因。我怀疑这句话是否适用于我的情况:

  • 使用无锁哈希表解析共享状态
  • 必须在消费者之间分散分配条目
  • 除了工作窃取之外,每个线程只在其本地队列中读写
更新-实现最高性能的底线:您需要使用惯用语法编写disruptor和work Desting,然后进行基准测试

对我来说,我认为区别主要在于信息和任务焦点之间的区别,因此也就是你想思考问题的方式。试着解决你们的问题,若它是以任务为中心的,那个么破坏者就是一个很好的选择。如果问题是以信息为中心的,那么您可能更适合使用另一种技术,例如工作窃取

  • 当您的实施以信息为中心时,请使用工作窃取。每个线程都可以拾取一条消息并运行它直到完成。对于示例HTTP服务器,每个入站HTTP请求分配一个线程。该线程专注于处理从开始到结束的请求—记录请求、检查安全控制、执行vhost查找、获取文件、发送响应和关闭连接

  • 当您的实施以任务为重点时,请使用中断器。每个线程都可以在特定的处理阶段工作。备选示例:对于任务焦点,处理将分为几个阶段,因此您将有一个执行日志记录的线程、一个用于安全控制的线程、一个用于vhost查找的线程,等等;每个线程专注于自己的任务,并将请求传递给管道中的下一个线程。阶段可能是并行的,但总体结构是一个专注于特定任务的线程,并在线程之间传递消息

当然,您可以更改实现以更好地适应每种方法

在您的情况下,如果您想使用Disruptor,我会以不同的方式构造问题。通常情况下,您可以通过让一个线程拥有状态并通过该工作线程传递所有任务来消除共享状态——在SEDA中查找大量类似的图表。这可能有很多好处,但同样,这取决于您的实现

更详细一些:

  • Disruptor-在需要严格的阶段顺序时非常有用,在所有任务长度一致时具有额外的好处,例如:外部系统上没有阻塞,每个任务的处理量非常相似。在这个场景中,您可以假设所有线程都将在系统中均匀地工作,因此每处理N条消息就安排N个线程。我喜欢将Disruptor看作是实现SEDA类系统的一种有效方法,其中线程处理阶段。当然,您可以有一个应用程序,其中一个阶段和多个并行单元在每个阶段执行相同的工作,但在我看来,这并不是真正的观点。这将完全避免共享状态的成本
  • 工作窃取-当任务的持续时间不同且消息处理顺序不重要时,使用此选项,因为这允许空闲且已消耗其消息的线程继续从另一个任务队列进行处理。这样,例如,如果您有10个线程,1个线程在IO上被阻塞,其余线程仍将完成其处理

谢谢您的回答。根据一些范例进行区分听起来很不错,不幸的是,我不知道消息焦点和任务焦点的区别。我确实设计了一个解决方案来解决我的干扰器问题:一个消费者用它们的散列值注释(多个)干扰器中的条目,另一个使用者将它们放在本地哈希表中,并用一个标志对条目进行注释,以确定它们是否已经在哈希表中。此解决方案可能更快,但不会像无锁哈希表那样在线程数量上线性扩展