坏蛋Dan
知乎@坏蛋Dan
发布时间:2024.1.4

前言

昨天我们稍微接触了下Stream,文档这部分应该也是还没完善的,说了几个方法就没了。

坏蛋Dan:rust基础学习--异步day5

今天我们接着学


一次执行多个Future

现在我们已经学会如何使用async/await了,也可以用于简单的异步场景了。

但是对于真实的异步情况来说一般需要并发的执行多个不同的异步运算。

接下来我们要接触一些方法涉及到上面的情况。

  • join!:等待所有futures都执行完毕。
  • select!:等待其中一个future完成。
  • Spawning:创建一个任务,它会携带一个future.(其实day2已经接触过了)
  • FuturesUnordered:一组future,它们从它们子future收取(yields)结果

join

futures::join这个宏的作用类似JoinHandlejoin方法,可以等待并发的多个不同的异步future完成再结束。

join!宏

先来看下例子

这个异步函数返回了两个异步任务,但是这其实没有想象中的块,因为它俩都是异步函数,get_music会被get_book堵塞。

这个时候我们就可以使用futures::join!来将它俩并发处理。

虽然输出的时候还是要等待它俩完成才会输出,但是它俩在等待的过程是并发的,所以不会有堵塞。

来稍微看下源码

是一个声明宏,给传入的异步任务分别调用futures_macro::join_internal的宏。

这是个属性宏

真正处理的源码是下面这个

简单的说这里将它俩合并成一个异步任务,然后等待它们完成。

原来的情况是music的会一直不动,直到第一个异步也就是bookready之后才会执行。

现在变成它俩同时进入执行器的接收器队列中,这样就不会堵塞了。

注意这里手动调用了futurepoll方法,所以外面也就不用调用await来处理了。

另外也不要这样做:其它语言中可以这样并发的做,但是在rust中不行,不会报错,但是依旧会堵塞。


try_join!

如果你的future返回的数据类型是Result的,那么这里最好用try_join!来代替join!

因为join!会一直执行下去,即使是这个数据返回的是Err变体。而try_join!会如果遇到Err变体会立即终止。

直接来看下用法

注意futureErr类型得是一样的。

我们可以用futures::future::TryFutureExt里的.err_into或者..map_err(|e| ...)来合并错误类型


select!

这个futures::select宏同样可以同时执行多个futures,不过不同于join!select!只要有其中一个完成就会返回。

有些类似前端promise.allpromise.race的区别。

default模式和complete模式用法

select!支持defaultcomplete两条分支:

  • default用在你模式啥都没匹配到但是已经结束咧~也就是都执行完了。
  • complete用在当所有分支都完成并且都被选择了的场景。一般select!搭配循环且在结束的时候被调用。

搭配Unpin和FusedFuture

上面第一个例子中,我们并没有提及.fuse这个方法。它用在原本应该是.await的地方。

它是必须调用的,因为使用select!的前提是future得实现UnpinFusedFuture这俩trait

实现Unpin的原因是select!的过程中并不是使用pin里包裹的值,而是一个可变引用,因为这样就不需要获取值的所有权,未完成的future就可以再次使用。

实现FusedFuture的原因类似Unpin,为了确保selectfuture执行结束之后不能再调用这个futurepollFusedFuture用来跟踪future是否已经完成。上面第二个例子中future::ready返回的future实现了FusedFuture,那么这个时候就不会再poll了。(Stream中也是差不多的:FusedStream

来看下例子

使用.next()/.try_next()将产生实现FusedFuturefuture


在select!循环中使用Fused和Unordered来并发任务

有一个很难描述但是很方便的函数叫Fuse::terminated(),它可以用来创建一个空的已经被终止的(terminated)future,这个空future将会被一个准备运行的future填充。

它能用于select循环过程中存储某个select自己产生的future。相当于select作用域外初始化一个future,等会在select循环过程中存储某个被创建的future

我们先来看下Fuse::terminated的例子

匹配到new_num的时候会被缓存到run_on_new_num_fut这个空future里。

当这个future完成后就会离开这个空future

或者这个future倒计时结束后依旧还没运行,这个时候也会被踢出去。

可以调用is_terminated判断当前是什么状态。

注意.select_next_some()方法只能用于selectbranchSome(_)模式的场景,None会直接忽略。

然后再来看个FuturesUnordered的例子

和第一个例子不同,这里所有的future(相同的future)都会被执行并且会输出结果


TODO: spawning

官方文档未完善


TODO: Cancellation and Timeouts

同上


TODO: FuturesUnordered

同上


总结

莫得总结

这一章很潦草,即使是非TODO的,也就是简单的说一下怎么用。

参考

  1. ^Executing-multiple-futures-at-a-time https://rust-lang.github.io/async-book/06_multiple_futures/01_chapter.html#executing-multiple-futures-at-a-time
  2. ^join https://rust-lang.github.io/async-book/06_multiple_futures/02_join.html#join
  3. ^join! https://rust-lang.github.io/async-book/06_multiple_futures/02_join.html#join-1
  4. ^try_join! https://rust-lang.github.io/async-book/06_multiple_futures/02_join.html#try_join
  5. ^select! https://rust-lang.github.io/async-book/06_multiple_futures/03_select.html#select
  6. ^https://rust-lang.github.io/async-book/06_multiple_futures/03_select.html#default---and-complete-- https://rust-lang.github.io/async-book/06_multiple_futures/03_select.html#default---and-complete--
  7. ^interation-with-Unpin-and-FusedFuture https://rust-lang.github.io/async-book/06_multiple_futures/03_select.html#interaction-with-unpin-and-fusedfuture

编辑于 2023-02-01 09:16・IP 属地广东