通过 RabbitMQ 取消 Rust 中长时间运行的 Rayon 任务的策略 [已关闭]

问题描述 投票:0回答:1

我正在用 Rust 开发一个执行器来处理计算密集型任务,作为大型 Web 应用程序的一部分。执行器从 RabbitMQ(使用 lapin)接收作业并处理各种 CPU 密集型操作,其中包括一些内部使用 rayon 的操作。我知道异步会使人造丝的使用变得复杂。计算后或任务取消后,它将结果或取消状态传达回 RabbitMQ。

所涉及的操作超出了我的控制范围,主要由长时间运行的并行图算法组成。我正在探索通过单独的 RabbitMQ 取消队列反应性取消这些任务的策略,而无需重写底层库,因为它们(据我理解)与异步模式不兼容,并且出于安全原因普遍缺乏内置任务取消支持。

我使用 tokio 制作了一个执行器原型,只是在事后才认识到 rayon 的潜在问题。目前的取消策略是基于 tokio oneshot 通道,它会低效地丢弃任务,而不会释放 CPU 资源。我正在寻求有关如何制作一个惯用的执行器的建议,该执行器可以有效地取消任务并符合 Rust 的安全保证。

编辑:我会尝试更具体:本质上,我正在寻找一种策略来从另一个线程取消(长时间运行、阻塞、CPU 限制)任务,有效地中止计算并释放 CPU。使用 tokio 的 oneshot 通道等机制优雅地发出关闭信号不是一个选择,因为我无法控制长时间运行操作的实现。

rust rabbitmq rust-tokio rayon
1个回答
2
投票

停止线程的唯一合理方法是让它与之合作。 有多种方法可以做到这一点,但它们都涉及一些普遍的东西:

  • Rust
    async
    代码可能会故意让出(从
    Poll::Pending
    返回
    poll()
    ),以便让执行者或包含未来的人有机会决定不再轮询它。
  • 您可以查阅原子标志或消息通道并返回或恐慌而不是继续。
  • 最后,“在某些情况下”可以进行安排,以便在线程执行突然终止的情况下,这不会破坏任何内容。但是,这需要完全控制线程执行的“所有”代码 - 线程不得调用任何第三方库。我听说过的唯一成功做到这一点的系统是“虚拟机”,其中代码由解释器或 JIT 管理。 因此,考虑到调用不受您控制的代码的限制,您最多能做的就是让线程继续运行,直到控制权返回到您编写的代码(此时您可以panic!()
  • 干净地终止线程)。

关于如何进行的一些想法:


如果您调用自身的代码使用 Rayon,那么也许您可以向 Rayon 提供取消功能(或运行修补版本),该功能将检查每个

rayon::join

或等效项上的取消。
  • 请记住,如果代码调用您提供的任何迭代器,这些也是您控制的代码,您可以安排恐慌。)
    向您正在调用的特定图书馆提供取消标志。
  • 您可能想知道:
  • 为什么
取消线程会破坏状态?两个例子:

让 Java 弃用其

Thread.stop()
    的经典示例是考虑线程持有的锁会发生什么情况:如果它们保持锁定状态,那么共享锁的任何内容本身都将永远被阻塞,从而造成挂起的蔓延线程;如果它们被解锁,则可能会显示无效的半编辑状态。 (在 Rust 中,我们有锁的“中毒”,但这并不能解决例如可能悬空的指针或其他类型的无效值;仅解决可能因恐慌解除而引起的应用程序级情况。)
  • 线程可以从其他线程的堆栈借用数据; Rayon 和 

    std::thread::scope()
  • 都这样做。突然取消线程会导致释放后使用。
  • 这两件事都可以通过添加适当的检查或禁止它们来解决(例如,严格使用通道和原子,不使用锁),但您必须确保

    线程执行的所有代码
  • 遵守适当的限制,而 Rust 作为一种没有“VM”或“运行时”的语言,不会在全球范围内施加此类限制。

© www.soinside.com 2019 - 2024. All rights reserved.