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

如何在async_scoped::TokioScope::scope_and_block中等待异步任务?

使用async_scoped时同步执行异步任务并获取返回值的解决方案

问题背景

需要通过async_scoped运行可访问非'static变量的线程,同时在同一个作用域内立即执行异步方法并处理其返回值。但scope_and_block创建的作用域并非异步环境,无法直接使用.await语法;尝试scope.spawn_blocking时,它无法直接接收异步函数,且无返回值;使用Handle::current().block_on()又会触发错误:

无法在运行时内部启动运行时。出现此问题是因为某个函数(如block_on)试图阻塞当前线程,而该线程正被用于驱动异步任务。

以下是原示例代码:

async fn need_async_value() -> i64 {
    5
}

async fn scoped_futures() {
    let not_copy = String::from("hello world!");
    let not_copy_ref = &not_copy;
    let (foo, outputs) = async_scoped::TokioScope::scope_and_block(|s| {
        for _ in 0..10 {
            let x = need_async_value().await; // <- 编译错误:不在异步块中
            let proc = || async {
                assert_eq!(not_copy_ref, "hello world!");
                eprintln!("Hello world!")
            };
            s.spawn(proc());
        }
        42
    });
    assert_eq!(foo, 42);
    assert_eq!(outputs.len(), 10);
}

解决方案

方案1:使用异步作用域scope替代scope_and_block

async_scoped提供的scope方法返回一个Future,可在异步上下文中.await,其闭包本身支持异步语法,能直接.await异步函数:

async fn need_async_value() -> i64 {
    5
}

async fn scoped_futures() {
    let not_copy = String::from("hello world!");
    let not_copy_ref = &not_copy;
    // 使用scope而非scope_and_block,闭包改为async闭包
    let (foo, outputs) = async_scoped::TokioScope::scope(|s| async {
        for _ in 0..10 {
            // 直接await异步函数获取返回值
            let x = need_async_value().await;
            println!("获取到异步返回值:{}", x);
            let proc = || async {
                assert_eq!(not_copy_ref, "hello world!");
                eprintln!("Hello world!")
            };
            s.spawn(proc());
        }
        42
    }).await;
    assert_eq!(foo, 42);
    assert_eq!(outputs.len(), 10);
}

方案2:在scope_and_block中使用spawn_local+block_on

若必须使用同步作用域scope_and_block,可结合tokio::task::spawn_local(需启用tokio的local特性)和futures::future::block_on,在当前线程的执行器中运行本地异步任务,避免启动新运行时:

use futures::future;
use tokio::task;

async fn need_async_value() -> i64 {
    5
}

async fn scoped_futures() {
    let not_copy = String::from("hello world!");
    let not_copy_ref = &not_copy;
    let (foo, outputs) = async_scoped::TokioScope::scope_and_block(|s| {
        for _ in 0..10 {
            // 提交本地异步任务并阻塞等待结果
            let x = future::block_on(task::spawn_local(need_async_value())).unwrap();
            println!("获取到异步返回值:{}", x);
            let proc = || async {
                assert_eq!(not_copy_ref, "hello world!");
                eprintln!("Hello world!")
            };
            s.spawn(proc());
        }
        42
    });
    assert_eq!(foo, 42);
    assert_eq!(outputs.len(), 10);
}

说明

  • 方案1更简洁,完全适配异步上下文,是优先推荐的方式;
  • 方案2适合必须使用同步作用域的场景,但需要在Cargo.toml中启用tokio的local特性:
    tokio = { version = "1.x", features = ["full", "local"] }
    futures = "0.3.x"
    

内容的提问来源于stack exchange,提问作者mentics

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 15:53:12