本文目录

  • hello world
  • executor
  • async
  • generator
  • await
  • future

futures库是很多人学习rust异步编程的第一站,今天我将通过一个简单的hello world程序,来揭开futures的执行细节。

本文使用rust 1.44.0 stable版本。

hello world

首先来一个异步执行的hello world:

use futures::executor;async fn hello() {println!("Hello, world!");
}fn main() {let fut = hello();executor::block_on(fut);
}

在hello函数打个断点,可以得出以下的调用栈:

hello::hello::{{closure}} main.rs:4
<core::future::from_generator::GenFuture<T> as core::future::future::Future>::poll mod.rs:66
futures_executor::local_pool::block_on::{{closure}} local_pool.rs:317
futures_executor::local_pool::run_executor::{{closure}} local_pool.rs:87
std::thread::local::LocalKey<T>::try_with local.rs:263
std::thread::local::LocalKey<T>::with local.rs:239
futures_executor::local_pool::run_executor local_pool.rs:83
futures_executor::local_pool::block_on local_pool.rs:317
hello::main main.rs:9
std::rt::lang_start::{{closure}} rt.rs:67
std::rt::lang_start_internal::{{closure}} rt.rs:52
std::panicking::try::do_call panicking.rs:331
std::panicking::try panicking.rs:274
std::panic::catch_unwind panic.rs:394
std::rt::lang_start_internal rt.rs:51
std::rt::lang_start rt.rs:67
main 0x0000000000401b2c
__tmainCRTStartup 0x00000000004013c7
mainCRTStartup 0x00000000004014fb

executor

我们从main函数开始分析,main函数调用了futures::executor::block_on()函数

hello::main main.rs:9

block_on()会阻塞当前的线程,直到future执行完成。
run_executor()的参数是一个闭包,负责调用传进来的future。

pub fn block_on<F: Future>(f: F) -> F::Output {pin_mut!(f);run_executor(|cx| f.as_mut().poll(cx))
}

run_executor()会poll传进来的future,如果future是ready的,则退出loop并返回future::Output;否则,将线程挂起(park),等待唤醒(unpark)。

// Set up and run a basic single-threaded spawner loop, invoking `f` on each
// turn.
fn run_executor<T, F: FnMut(&mut Context<'_>) -> Poll<T>>(mut f: F) -> T {CURRENT_THREAD_NOTIFY.with(|thread_notify| {let waker = waker_ref(thread_notify);let mut cx = Context::from_waker(&waker);loop {if let Poll::Ready(t) = f(&mut cx) {return t;}// Consume the wakeup that occurred while executing `f`, if any.let unparked = thread_notify.unparked.swap(false, Ordering::Acquire);if !unparked {// No wakeup occurred. It may occur now, right before parking,// but in that case the token made available by `unpark()`// is guaranteed to still be available and `park()` is a no-op.thread::park();// When the thread is unparked, `unparked` will have been set// and needs to be unset before the next call to `f` to avoid// a redundant loop iteration.thread_notify.unparked.store(false, Ordering::Release);}}})
}

CURRENT_THREAD_NOTIFY是一个线程局部存储变量,由于ThreadNotify结构实现了ArcWake trait,所以可以通过futures::task::waker_ref()获得WakerRef,进而构造出context。

pub(crate) struct ThreadNotify {/// The (single) executor thread.thread: Thread,/// A flag to ensure a wakeup (i.e. `unpark()`) is not "forgotten"/// before the next `park()`, which may otherwise happen if the code/// being executed as part of the future(s) being polled makes use of/// park / unpark calls of its own, i.e. we cannot assume that no other/// code uses park / unpark on the executing `thread`.unparked: AtomicBool,
}thread_local! {static CURRENT_THREAD_NOTIFY: Arc<ThreadNotify> = Arc::new(ThreadNotify {thread: thread::current(),unparked: AtomicBool::new(false),});
}impl ArcWake for ThreadNotify {fn wake_by_ref(arc_self: &Arc<Self>) {// Make sure the wakeup is remembered until the next `park()`.let unparked = arc_self.unparked.swap(true, Ordering::Relaxed);if !unparked {// If the thread has not been unparked yet, it must be done// now. If it was actually parked, it will run again,// otherwise the token made available by `unpark`// may be consumed before reaching `park()`, but `unparked`// ensures it is not forgotten.arc_self.thread.unpark();}}
}

async

我们都听过,使用async修饰函数时,相当于返回一个实现了Future trait的trait object。比如上面的hello()等价于

fn hello() -> impl Future<Output = ()> {async {println!("Hello, world!");}
}

但是如果hello()里面有阻塞操作,比如睡眠了几秒钟,它是如何交出执行控制权?

async fn hello() {async_std::task::sleep(Duration::from_secs(3)).await;println!("Hello, world!");
}

答案是rust通过generator的yield实现跳转,进而实现交出执行控制权。

generator

Generator是rust异步执行的基础,在上文中,hello()的函数体会成为generator::resume的函数体,如果hello() 阻塞在操作,则由await调用generator的yield返回。

因此,使用async修饰函数时,其实是实现了一个包裹着generator的Future,这一点可以从代码得到验证。

/// Wrap a generator in a future.
///
/// This function returns a `GenFuture` underneath, but hides it in `impl Trait` to give
/// better error messages (`impl Future` rather than `GenFuture<[closure.....]>`).
pub const fn from_generator<T>(gen: T) -> impl Future<Output = T::Return>
whereT: Generator<ResumeTy, Yield = ()>,
{struct GenFuture<T: Generator<ResumeTy, Yield = ()>>(T);// We rely on the fact that async/await futures are immovable in order to create// self-referential borrows in the underlying generator.impl<T: Generator<ResumeTy, Yield = ()>> !Unpin for GenFuture<T> {}impl<T: Generator<ResumeTy, Yield = ()>> Future for GenFuture<T> {type Output = T::Return;fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {// Safety: Safe because we're !Unpin + !Drop, and this is just a field projection.let gen = unsafe { Pin::map_unchecked_mut(self, |s| &mut s.0) };// Resume the generator, turning the `&mut Context` into a `NonNull` raw pointer. The// `.await` lowering will safely cast that back to a `&mut Context`.match gen.resume(ResumeTy(NonNull::from(cx).cast::<Context<'static>>())) {GeneratorState::Yielded(()) => Poll::Pending,GeneratorState::Complete(x) => Poll::Ready(x),}}}GenFuture(gen)
}

await

从上面的描述,不难想到,await其实是一个小型的executor,它会loop所等待的future,如果ready了,直接返回Future::Output,否则,通过generator::yield跳转,进而交出执行控制权。

loop {match polling_future_block() {Pending => yield,Ready(x) => break x}
}

future

future是可以嵌套的,也就是一个future可以包含另外一个future,比如上文中的task::sleep函数,调用形式是future.await,其实质是await去poll另外一个future。

如果最顶层的future阻塞了,线程将会被挂起(park),等待reactor唤醒。

rust future async/await相关推荐

  1. rust异步编程--理解并发/多线程/回调/异步/future/promise/async/await/tokio

    1. 异步编程简介 通常我们将消息通信分成同步和异步两种: 同步就是消息的发送方要等待消息返回才能继续处理其它事情 异步就是消息的发送方不需要等待消息返回就可以处理其它事情 很显然异步允许我们同时做更 ...

  2. 【Rust日报】 2019-08-12:Tokio alpha 版发布,新版本支持async/await

    Tokio alpha 版发布 #tokio 新版本支持async/await tokio = "=0.2.0-alpha.1" #![feature(async_await)]u ...

  3. ttf_openfont可以多次调用吗_【译文】Rust futures: async fn中的thread::sleep和阻塞调用...

    原文:Rust futures: thread::sleep and blocking calls inside async fn URL: https://blog.hwc.io/posts/rus ...

  4. 一文读懂Rust的async

    一文读懂Rust的async 不同的编程语言表现异步编程的方式可能不一样,Rust跟JavaScript的async/await类似:使用的关键字啊,编程模型啊都差不多啦! 也有些不一样的地方,毕竟R ...

  5. Rust futures: async fn 中的 thread::sleep 和阻塞调用

    原文:Rust futures: thread::sleep and blocking calls inside async fn URL: https://blog.hwc.io/posts/rus ...

  6. 深入浅出Rust Future - Part 1

    本文译自Rust futures: an uneducated, short and hopefully not boring tutorial - Part 1,时间:2018-12-02,译者: ...

  7. Python 异步 IO 、协程、asyncio、async/await、aiohttp

    From :廖雪峰 异步IO :https://www.liaoxuefeng.com/wiki/1016959663602400/1017959540289152 Python Async/Awai ...

  8. Async/Await FAQ

    From time to time, I receive questions from developers which highlight either a need for more inform ...

  9. python 异步 async/await -1.一文理解什么是协程

    前言 Python 在 3.5 版本中引入了关于协程的语法糖 async 和 await, 在 python3.7 版本可以通过 asyncio.run() 运行一个协程. 所以建议大家学习协程的时候 ...

最新文章

  1. Typora输出表情 Typora_Smile
  2. R语言如何将字符串转变为命令执行
  3. Clusterware 和 RAC 中的域名解析的配置校验和检查 (文档 ID 1945838.1)
  4. java的知识点33——死锁及解决方案
  5. 转 :Vim文件编码识别与乱码处理
  6. 可视化神器背后的奥秘
  7. IOS 实现QQ好友分组展开关闭功能
  8. 【08月14日】A股ROE最高排名
  9. arch linux界面优化,Archlinux 启动优化
  10. html5 应用框架,基于HTML5移动应用框架的研究及应用
  11. Chrome本地安装vue-devtools调试工具的问题
  12. 【MySQL】【高可用】从masterha_master_switch工具简单分析MHA的切换逻辑
  13. 李飞飞新动向:创建斯坦福“以人为本AI研究院”,担任共同院长
  14. oracle 自定义比较函数
  15. ACR122U写卡软件v3.6绿色版
  16. 企业权限管理系统之AdminLTE的基本介绍(一)
  17. vue之仿网易严选详解
  18. MPEG4 协议详解
  19. 海康大华摄像头GB/T28181接入国标视频平台如何选择主码流还是子码流
  20. 记录一次解决后端接口设置cookie设置不上去经过,一级域名可以设置上去cookie,二级域名设置不上cookie

热门文章

  1. visio画等分树状图
  2. 如何在PCB中放置禁止触摸标志
  3. 720P、1080P、1440P、2160P、HD、FHD、UHD、2K屏、4K屏是什么意思
  4. 虚拟机双硬盘安装ubuntu固态+机械
  5. oracle中取月初,月末,季初,季末及年初,年末时间
  6. Unsupervised Domain Adaptive Re-Identification: Theory and Practice阅读总结
  7. 电脑c盘分区太小如何可以扩大,电脑c盘不够用了,如何给电脑分区
  8. svga插件_如何压缩SVGA格式的礼物特效文件
  9. Python实现股票量化交易学习进阶(二)之简单交易策略的定义实现
  10. 如何把catia完全卸载干净_catia软件卸载不了怎么办