使用Runtime::block_on时Tokio signal::ctrl_c触发panic求助
问题原因
手动通过Builder::new_multi_thread()创建的Tokio Runtime默认仅启用IO和时间驱动,未启用信号驱动(signal driver),而tokio::signal::ctrl_c()必须依赖信号驱动才能监听系统信号。相比之下,#[tokio::main]宏创建的Runtime默认会启用所有必要驱动(包括信号驱动),因此使用该宏时代码可正常运行。
解决方案
在构建Runtime时,显式调用enable_signal()方法启用信号驱动,或者使用enable_all()启用所有Runtime功能:
修改后的Runtime构建代码
let runtime = Builder::new_multi_thread() .worker_threads(2) .enable_signal() // 显式启用信号驱动 .build().unwrap();
或者启用所有驱动:
let runtime = Builder::new_multi_thread() .worker_threads(2) .enable_all() .build().unwrap();
额外优化
代码中let _rt_guard = runtime.enter();是多余的,runtime.block_on()已经会将当前线程纳入Runtime的上下文环境,无需额外调用enter(),可以直接删除该语句。
修改后的完整代码
use tokio::runtime::Builder; use tokio::signal; use tokio::sync::watch; use tokio::task::JoinSet; fn main() { let runtime = Builder::new_multi_thread() .worker_threads(2) .enable_signal() .build().unwrap(); runtime.block_on(async { let (ping_tx, mut ping_rx) = watch::channel(0u32); let (pong_tx, mut pong_rx) = watch::channel(0u32); let mut tasks = JoinSet::new(); let ping = tasks.spawn(async move { let mut val = 0u32; ping_tx.send(val).unwrap(); while val < 10u32 { pong_rx.changed().await.unwrap(); val = *pong_rx.borrow(); ping_tx.send(val + 1).unwrap(); println!("ping! {}", val + 1); } }); let pong = tasks.spawn(async move { let mut val = 0u32; while val < 10u32 { ping_rx.changed().await.unwrap(); val = *ping_rx.borrow(); pong_tx.send(val + 1).unwrap(); println!("pong! {}", val + 1); } }); let mut interrupt = Box::pin(signal::ctrl_c()); let mut interrupt_read = false; while !interrupt_read && !tasks.is_empty() { tokio::select! { biased; _ = &mut interrupt, if !interrupt_read => { ping.abort(); pong.abort(); interrupt_read = true; }, _ = tasks.join_next() => {} } } }); }
内容的提问来源于stack exchange,提问作者hal7df
相关产品推荐
相关产品推荐

