如何在 Tokio 中为 CPU 密集型工作创建专用线程池?

Kor*_*nel 10 threadpool rust rust-tokio

我有一个基于Tokio的 Rust 异步服务器运行时。它必须同时处理对延迟敏感的 I/O 密集型请求和大量 CPU 密集型请求。

我不想让 CPU 密集型任务垄断 Tokio 运行时并使 I/O 密集型任务饿死,所以我想将 CPU 密集型任务卸载到专用的、隔离的线程池(隔离是这里的关键,所以spawn_blocking/block_in_place在一个共享线程池上是不够的)。如何在 Tokio 中创建这样的线程池?

启动两个运行时的幼稚方法会遇到错误:

线程“tokio-runtime-worker”因“无法从运行时内启动运行时”而恐慌。发生这种情况是因为一个函数(如block_on)试图在当前线程被用于驱动异步任务时阻塞当前线程。

use tokio; // 0.2.20

fn main() {
    let mut main_runtime = tokio::runtime::Runtime::new().unwrap();
    let cpu_pool = tokio::runtime::Builder::new().threaded_scheduler().build().unwrap();
    let cpu_pool = cpu_pool.handle().clone(); // this is the fix/workaround!

    main_runtime.block_on(main_runtime.spawn(async move {
        cpu_pool.spawn(async {}).await
    }))
    .unwrap().unwrap();
}
Run Code Online (Sandbox Code Playgroud)

Tokio 可以允许两个独立的运行时吗?有没有更好的方法在 Tokio 中创建隔离的 CPU 池?

Sta*_*eur 11

虽然 Tokio 已经有一个线程池,但Tokio的文档建议:

如果您的代码受 CPU 限制,并且您希望限制用于运行它的线程数,则应在另一个线程池(例如rayon )上运行它。当人造丝任务完成时,您可以使用oneshot通道将结果发送回 Tokio 。

所以,如果你想创建一个线程池来大量使用 CPU,一个好方法是使用像 Rayon 这样的 crate 并将结果发送回 Tokio 任务。

  • [`spawn_blocking`](https://docs.rs/tokio/0.2.20/tokio/task/fn.spawn_blocking.html) 同样说:*要仅在几个线程上运行 CPU 密集型计算,您应该使用一个单独的线程池,例如rayon,而不是配置阻塞线程的数量。* (2认同)
  • 使用人造丝需要通过异步通道将结果发送回,这非常麻烦;( (2认同)

Kor*_*nel 8

Tokio 的错误消息具有误导性。该问题是由于Runtime对象在异步上下文中被删除造成的。

解决方法是使用Handle(而不是Runtime直接)在其他运行时上生成任务。

fn main() {
    let mut main_runtime = tokio::runtime::Runtime::new().unwrap();
    let cpu_pool = tokio::runtime::Builder::new().threaded_scheduler().build().unwrap();

    // this is the fix/workaround:
    let cpu_pool = cpu_pool.handle().clone(); 

    main_runtime.block_on(main_runtime.spawn(async move {
        cpu_pool.spawn(async {}).await
    }))
    .unwrap().unwrap();
}
Run Code Online (Sandbox Code Playgroud)

  • 我看不出在您的上下文中创建另一个运行时的意义。 (2认同)

She*_*ter 5

启动 Tokio 运行时已经创建了一个线程池。相关选项是

粗略地说,core_threads控制将使用多少线程来处理异步代码。max_threads-core_threads是有多少线程将用于阻塞工作(重点是我的):

否则,core_threads由于始终处于活动状态,它将附加线程(例如用于阻塞注释)限制为max_threads- core_threads。

您还可以通过tokio::main属性指定这些选项。

然后,您可以使用以下任一方式注释阻塞代码:

也可以看看:

spawn_blocking 可以轻松地获取在一个且唯一的运行时中可用的所有线程,迫使其他期货等待它们

Semaphore在这种情况下,您可以使用诸如 a 之类的技术来限制最大并行度。

  • 这并不能回答我的问题。我正在寻找一个**专用**线程池,它不与我的主运行时共享线程。我已经在使用“block_in_place”,但这会破坏我的延迟,并且阻塞的线程会导致网络连接匮乏。 (3认同)
  • 因为我有许多非常长的 CPU 密集型任务,所以“spawn_blocking”可以轻松地获取唯一运行时中可用的所有线程,迫使其他 future 等待它们。 (2认同)