Actix-web中async-channel recv未触发问题及Tokio兼容疑问
Actix-web中async-channel测试问题与兼容性解答
问题描述
在actix-web中复现async-channel的简单基准测试时,遇到以下问题:
- recv函数始终未被调用
- 发送线程中的await从未触发Tokio切换到另一个线程,原预期await会让出当前上下文
- 已尝试将代码块移出spawn调用、用select替代
join!、移除join!等操作,问题仍存在
同时想确认:actix-web与Tokio是否兼容,比如在actix-web中使用tokio::spawn,以及在tokio::main中使用actix::rt::spawn是否可行。
测试代码如下:
#[actix_web::main] async fn main() { let (s, r) = async_channel::unbounded(); let t1 = tokio::spawn(async move { let mut now = chrono::Utc::now(); while let Ok(x) = r.recv().await { if x % 1 == 0 { let next_now = chrono::Utc::now(); println!("{} {:?}", x, next_now - now); now = next_now; } } }); let t2 = tokio::spawn(async move { for i in 1.. { s.send(i).await.unwrap(); } }); join!(t1, t2).await; }
问题原因
- 无界通道导致send无挂起:你使用的
async_channel::unbounded()是无界通道,这种通道的send().await几乎不会进入挂起状态——因为它可以无限存储消息,send操作会立即完成,根本不会触发await的上下文切换。 - 发送线程抢占资源:发送线程的循环会疯狂执行send操作,持续占用Tokio的工作线程,导致接收线程完全得不到调度执行的机会,自然不会进入recv的逻辑。
解决方法
- 使用有界通道:将通道改为
async_channel::bounded(1)(或其他较小的缓冲区大小),当通道满时,send().await会挂起,主动让出线程资源,接收线程就能被调度执行。 - 给发送逻辑添加延迟:比如在send后添加
tokio::time::sleep(std::time::Duration::from_micros(1)).await,强制触发上下文切换,让接收线程有运行机会。
Actix-web与Tokio的兼容性说明
- 在actix-web中使用
tokio::spawn完全可行:#[actix_web::main]宏本质上是封装了带Actix配置的Tokio运行时,直接用tokio::spawn启动任务没有任何问题。 - 在
tokio::main中使用actix::rt::spawn也兼容:Actix的runtime是基于Tokio的上层封装,只要当前运行时是标准的Tokio运行时,actix::rt::spawn可以正常工作,它会将任务提交到Tokio运行时中执行。
内容的提问来源于stack exchange,提问作者Test
相关产品推荐
相关产品推荐

