如何在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 = ¬_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 = ¬_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 = ¬_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
相关产品推荐
相关产品推荐

