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

前言

昨天我们知道了异步执行的流程/原理

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

今天我们可以开始学怎么用了


async/.await

在之前day1的引子以及昨天的定时器例子中我们有使用到它俩,今天我们再来看下细节。

和同步的堵塞代码不同,异步可以绕开堵塞的问题,让线程先绕过这块事件先去执行别的。

我么来看下例子

这是个简单的场景。

注意异步函数返回的内容不一定是Future<output>类型的,如果不搭配.await的话。

可以类比前端async/await的用法,在不用await的时候返回的是一个promise类型的数据。

我们上一节知道这个过程是lazy的,只有被waketask才开始干活。

如果它执行时判断不是Ready状态,这个taskfuture就会让出当前线程的控制权,让它去被别的任务占有,直到它所在的task被唤醒,又开始争夺正宫的位置。

async的生命周期

和传统的函数不同,异步函数默认会将引用或者非'static的参数作为返回类型Feture<output>的生命周期(逻辑与运算,作为lifetime bound)

比如

一般上下文中是不太需要担心的,但是如果涉及到多线程之间或者多任务之间,那就比较复杂了。

将引用作为参数的异步数转换为'static Future的一种常见解决方法是将参数与异步块中对异步函数的调用捆绑在一起:

我们把x和异步函数绑到了一起,这样这个x的引用的作用域就和异步函数调用的作用域一样了。


async move

异步块(block)/闭包都可以用move这个关键字,和常规闭包有些像。一个异步move的块将会获取引用变量的所有权,它允许这个变量的生命周期比当前block的长,但是不允许它被其他代码使用。

来看个例子

因为异步的原因导致这个数据不知道什么时候才会被用到,所以为了保证不会数据竞争直接不允许其它地方访问这个数据,即使这个时候这个异步块已经await完返回了,既然block都没了也自然被释放了。


多线程执行器中使用.await

我们之前有提到过这一点,在多线程的场景下,一个future在不同线程之前穿插。这就需要保证这个future依赖的数据也是能穿插的,也就是实现SendSync这俩trait

如果没有,那这将会是unsafe的。即使是加锁,加锁还有可能造成死锁。

为了避免这些场景,尽量用futures::lock里的Mutex替代std::syncMutex


总结

今天的好像还是理论偏多。。。。。

参考

  1. ^async/.await https://rust-lang.github.io/async-book/03_async_await/01_chapter.html#asyncawait
  2. ^async-lifetimes https://rust-lang.github.io/async-book/03_async_await/01_chapter.html#async-lifetimes
  3. ^async move https://rust-lang.github.io/async-book/03_async_await/01_chapter.html#async-move
  4. ^.await-on-multithread-executor https://rust-lang.github.io/async-book/03_async_await/01_chapter.html#awaiting-on-a-multithreaded-executor

发布于 2023-01-29 23:30・IP 属地广东