Concurrency 从多个其他任务通知任务而无需额外工作
我的应用程序基于futures,使用async/await,并且在其中一个组件中具有以下结构:Concurrency 从多个其他任务通知任务而无需额外工作,concurrency,rust,async-await,channel,Concurrency,Rust,Async Await,Channel,我的应用程序基于futures,使用async/await,并且在其中一个组件中具有以下结构: “经理”,负责根据外部输入和“工人”的当前状态启动/停止/重新启动“工人” 一组动态的“工人”,他们执行一些连续的工作,但可能会失败或在外部停止 工人只是一个做一些I/O工作的衍生工人。在内部,它是一个无限循环,但由于错误或其他原因,它可能会提前退出,在这种情况下,管理者必须从头开始重新启动worker 管理器被实现为一个循环,它在多个通道上等待,包括一个由返回的通道,这基本上使管理器成为一个轮询器—
互斥锁保护的外部状态。基于这种状态,管理者,除其他外,创建或销毁其员工
此外,管理器存储一组表示活动工作人员的s,并使用这些句柄检查是否有工作人员已退出,如果有,则重新启动。(顺便说一句,我目前使用select(handle,future::ready())
,这是完全次优的,因为它依赖于select
实现细节,特别是它首先轮询左未来。我找不到更好的方法;类似这样做更有意义,但是race()
消耗两个未来,这对我不起作用,因为我不想失去JoinHandle
,如果它还没有准备好。不过,这是另一个问题。)
您可以看到,在此设计中,只有在管理器中发生下一次轮询“勾选”时,工人才能重新启动。但是,我不想使用太小的轮询间隔,因为在大多数情况下,轮询只会浪费CPU周期。但是,长时间间隔可能会使重新启动失败/取消的工作进程延迟太多,从而导致意外的延迟。因此,我想我已经设置了另一个从每个worker返回到manager的()
通道,我将其添加到主manager循环中,因此当worker由于错误或其他原因停止时,它将首先向其通道发送消息,导致经理在下一次轮询之前被唤醒,以便立即重新启动工作进程
不幸的是,对于任何类型的渠道,如果两个或多个工作人员几乎同时停止(由于我的应用程序的性质,这在某种程度上可能会发生),这可能会导致超过需要的轮询。在这种情况下,只运行一次manager循环,处理所有停止的工作人员是有意义的,但是通过通道,它必然会导致轮询的数量等于停止的工作人员的数量,即使额外的轮询没有任何作用
因此,我的问题是:我如何从员工那里通知经理他们已经完成了,而不会在经理中产生额外的投票?我试过以下几件事:
如上所述,常规无界通道无法工作
我认为可能有界通道可以工作-如果我使用容量为0的通道,并且有一种方法可以尝试向其中发送消息,但如果通道已满,则只需删除消息(如Java的BlockingQueue
),这似乎可以解决问题。不幸的是,channels API虽然提供了这样一种方法(try_send()
),但它也具有容量大于或等于发送者数量的特性,这意味着它不能真正用于此类通知
某种原子或互斥保护的布尔标志看起来也可以工作,但没有原子或互斥API可以提供未来等待,也需要轮询
重新构造manager实现,将JoinHandle
s包含到主select
中。它可能会起作用,但它会导致大规模的重构,这是我现在不愿意做的。如果有一种方法可以在不进行重构的情况下实现我想要的,我想先使用它
我想原子和通道的某种组合可能会起作用,比如设置原子标志并发送消息,然后根据标志跳过管理器中的任何额外通知(在处理一个通知后,该标志会翻转回off),但这似乎也是一种复杂的方法,我想知道有没有更简单的办法
我建议使用futures
板条箱中的类型。此集合允许您将同一类型的多个未来推送到一个集合中,并等待其中任何一个同时完成
它实现了流
,因此,如果导入,可以使用无序.next()
获得一个未来,该未来在集合中的任何未来完成后即完成
如果还需要等待超时或互斥等,则可以使用创建一个在超时或一个连接句柄完成后完成的未来。next()
返回的future实现了Unpin
,因此它可以与select
一起使用,不会出现问题。我建议使用futures
板条箱中的类型。此集合允许您将同一类型的多个未来推送到一个集合中,并等待其中任何一个同时完成
它实现了流
,因此,如果导入,可以使用无序.next()
获得一个未来,该未来在集合中的任何未来完成后即完成
如果还需要等待超时或互斥等,则可以使用创建一个在超时或一个连接句柄完成后完成的未来。next()
返回的future实现了Unpin
,因此它可以与select
一起使用,不会出现问题。