You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
}

问题原因

  1. 无界通道导致send无挂起:你使用的async_channel::unbounded()是无界通道,这种通道的send().await几乎不会进入挂起状态——因为它可以无限存储消息,send操作会立即完成,根本不会触发await的上下文切换。
  2. 发送线程抢占资源:发送线程的循环会疯狂执行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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 03:45:41