gpt4 book ai didi

asynchronous - 为什么在 crossbeam_channel::select 旁边调用时 tokio::spawn 有延迟?

转载 作者:行者123 更新时间:2023-12-03 11:26:29 25 4
gpt4 key购买 nike

我正在创建一个将产生其他任务的任务。其中一些需要一些时间,因此无法等待,但可以并行运行:
src/main.rs

use crossbeam::crossbeam_channel::{bounded, select};

#[tokio::main]
async fn main() {
let (s, r) = bounded::<usize>(1);

tokio::spawn(async move {
let mut counter = 0;
loop {
let loop_id = counter.clone();
tokio::spawn(async move { // why this one was not fired?
println!("inner task {}", loop_id);
}); // .await.unwrap(); - solves issue, but this is long task which cannot be awaited
println!("loop {}", loop_id);
select! {
recv(r) -> rr => {
// match rr {
// Ok(ee) => {
// println!("received from channel {}", loop_id);
// tokio::spawn(async move {
// println!("received from channel task {}", loop_id);
// });
// },
// Err(e) => println!("{}", e),
// };
},
// more recv(some_channel) ->
}
counter = counter + 1;
}
});

// let s_clone = s.clone();
// tokio::spawn(async move {
// s_clone.send(2).unwrap();
// });

loop {
// rest of the program
}
}
我注意到奇怪的行为。这输出:
loop 0
我期待它也能输出 inner task 0 .
如果我向 channel 发送一个值,输出将是:
loop 0
inner task 0
loop 1
缺少 inner task 1 .
为什么是 inner task产生一个延迟循环?
我第一次注意到“从 channel 任务接收”的这种行为延迟了一个循环,但是当我减少代码以准备样本时,这种情况开始发生在“内部任务”中。值得一提的是,如果我写第二个 tokio::spawn对另一个,只有最后一个会有这个问题。调用 tokio::spawn 时有什么需要注意的吗?和 select! ?是什么导致这一循环延迟?
Cargo.toml 依赖项
[dependencies]
tokio = { version = "0.2", features = ["full"] }
crossbeam = "0.7"
使用rust 1.46,Windows 10

最佳答案

select!正在阻塞,tokio::spawn 的文档say :

The spawned task may execute on the current thread, or it may be sent to a different thread to be executed.


在这种情况下, select! “future”实际上是一个阻塞函数,而 spawn不使用新线程(在第一次调用或循环内)。
因为你没有告诉 tokio 你要阻塞,所以 tokio 认为不需要另一个线程(从 tokio 的角度来看,你只有 3 个永远不应该阻塞的 future ,所以你为什么还需要另一个线程呢?)。
解决方案是使用 tokio::task::spawn_blocking 对于 select! -ing 关闭(这将不再是 future ,所以 async move {} 现在是 move || {} )。
现在 tokio 将知道这个函数实际上是阻塞的,并将它移动到另一个线程(同时将所有实际的 future 保留在其他执行线程中)。
use crossbeam::crossbeam_channel::{bounded, select};

#[tokio::main]
async fn main() {
let (s, r) = bounded::<usize>(1);

tokio::task::spawn_blocking(move || {
// ...
});

loop {
// rest of the program
}
}
Link to playground
另一种可能的解决方案是使用像 tokio::sync::mpsc 这样的非阻塞 channel 。 , 你可以在上面使用 await并获得预期的行为,例如 playground example直接 recv().await或与 tokio::select! , 像这样:
use tokio::sync::mpsc;

#[tokio::main]
async fn main() {
let (mut s, mut r) = mpsc::channel::<usize>(1);

tokio::spawn(async move {
loop {
// ...
tokio::select! {
Some(i) = r.recv() => {
println!("got = {}", i);
}
}
}
});

loop {
// rest of the program
}
}
Link to playground

关于asynchronous - 为什么在 crossbeam_channel::select 旁边调用时 tokio::spawn 有延迟?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64126518/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com