Concurrency 从多个其他任务通知任务而无需额外工作

Concurrency 从多个其他任务通知任务而无需额外工作,concurrency,rust,async-await,channel,Concurrency,Rust,Async Await,Channel,我的应用程序基于futures,使用async/await,并且在其中一个组件中具有以下结构: “经理”,负责根据外部输入和“工人”的当前状态启动/停止/重新启动“工人” 一组动态的“工人”,他们执行一些连续的工作,但可能会失败或在外部停止 工人只是一个做一些I/O工作的衍生工人。在内部,它是一个无限循环,但由于错误或其他原因,它可能会提前退出,在这种情况下,管理者必须从头开始重新启动worker 管理器被实现为一个循环,它在多个通道上等待,包括一个由返回的通道,这基本上使管理器成为一个轮询器—

我的应用程序基于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
    一起使用,不会出现问题。