首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在循环中生成异步方法?

如何在循环中生成异步方法?
EN

Stack Overflow用户
提问于 2020-08-16 08:57:29
回答 1查看 12.2K关注 0票数 10

我有一个对象向量,它有一个resolve()方法,该方法使用reqwest查询外部web。在对每个对象调用resolve()方法之后,我希望打印每个请求的结果。

下面是我的半异步代码,它编译和工作(但不是真正异步的):

代码语言:javascript
复制
for mut item in items {
    item.resolve().await;

    item.print_result();
}

我试图使用tokio::join!生成所有异步调用并等待它们完成,但我可能做错了什么:

代码语言:javascript
复制
tokio::join!(items.iter_mut().for_each(|item| item.resolve()));

下面是我遇到的错误:

代码语言:javascript
复制
error[E0308]: mismatched types
  --> src\main.rs:25:51
   |
25 |     tokio::join!(items.iter_mut().for_each(|item| item.resolve()));
   |                                                   ^^^^^^^^^^^^^^ expected `()`, found opaque type
   | 
  ::: src\redirect_definition.rs:32:37
   |
32 |     pub async fn resolve(&mut self) {
   |                                     - the `Output` of this `async fn`'s found opaque type
   |
   = note: expected unit type `()`
            found opaque type `impl std::future::Future`

如何同时调用所有实例的resolve()方法?

这段代码反映了答案--现在我正在处理的是我并不真正理解的借入检查器错误--我是否应该用'static注释我的一些变量

代码语言:javascript
复制
let mut items = get_from_csv(path);

let tasks: Vec<_> = items
    .iter_mut()
    .map(|item| tokio::spawn(item.resolve()))
    .collect();

for task in tasks {
    task.await;
}

for item in items {
    item.print_result();
}
代码语言:javascript
复制
error[E0597]: `items` does not live long enough
  --> src\main.rs:18:25
   |
18 |       let tasks: Vec<_> = items
   |                           -^^^^
   |                           |
   |  _________________________borrowed value does not live long enough
   | |
19 | |         .iter_mut()
   | |___________________- argument requires that `items` is borrowed for `'static`
...
31 |   }
   |   - `items` dropped here while still borrowed

error[E0505]: cannot move out of `items` because it is borrowed
  --> src\main.rs:27:17
   |
18 |       let tasks: Vec<_> = items
   |                           -----
   |                           |
   |  _________________________borrow of `items` occurs here
   | |
19 | |         .iter_mut()
   | |___________________- argument requires that `items` is borrowed for `'static`
...
27 |       for item in items {
   |                   ^^^^^ move out of `items` occurs here
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-08-16 13:29:33

因为您希望并行地等待未来的到来,所以可以将它们产卵为并行运行的单个任务。由于它们彼此独立运行,并且独立于生成它们的线程,所以您可以以任何顺序等待它们的句柄。

理想情况下,您应该写这样的东西:

代码语言:javascript
复制
// spawn tasks that run in parallel
let tasks: Vec<_> = items
    .iter_mut()
    .map(|item| tokio::spawn(item.resolve()))
    .collect();
// now await them to get the resolve's to complete
for task in tasks {
    task.await.unwrap();
}
// and we're done
for item in &items {
    item.print_result();
}

但是,这将被借入检查器拒绝,因为item.resolve()返回的未来包含对item的借用引用。引用被传递给tokio::spawn(),后者将它交给另一个线程,编译器无法证明item将超过该线程。(当您想要将对本地数据的引用发送到线程时,也会遇到相同的问题。)

对此有几种可能的解决方案;我认为最优雅的解决方案是将项移到传递给tokio::spawn()的异步闭包中,并在任务完成后将它们交给您。基本上,您需要使用items向量来创建任务,然后根据等待的结果立即对其进行重构:

代码语言:javascript
复制
// note the use of `into_iter()` to consume `items`
let tasks: Vec<_> = items
    .into_iter()
    .map(|mut item| {
        tokio::spawn(async {
            item.resolve().await;
            item
        })
    })
    .collect();
// await the tasks for resolve's to complete and give back our items
let mut items = vec![];
for task in tasks {
    items.push(task.await.unwrap());
}
// verify that we've got the results
for item in &items {
    item.print_result();
}

游乐场中运行的代码。

请注意,futures机箱包含一个与您所需要的类似的join_all函数,除非它轮询个人的未来,而不确保它们并行运行。我们可以编写一个通用的join_parallel,它使用join_all,但也可以使用tokio::spawn来获得并行执行:

代码语言:javascript
复制
async fn join_parallel<T: Send + 'static>(
    futs: impl IntoIterator<Item = impl Future<Output = T> + Send + 'static>,
) -> Vec<T> {
    let tasks: Vec<_> = futs.into_iter().map(tokio::spawn).collect();
    // unwrap the Result because it is introduced by tokio::spawn()
    // and isn't something our caller can handle
    futures::future::join_all(tasks)
        .await
        .into_iter()
        .map(Result::unwrap)
        .collect()
}

使用这个函数,回答问题所需的代码可以归结为:

代码语言:javascript
复制
let items = join_parallel(items.into_iter().map(|mut item| async {
    item.resolve().await;
    item
})).await;
for item in &items {
    item.print_result();
}

同样,在游乐场中运行代码。

票数 12
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/63434977

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档