Skip to content

9月20日学习笔记

lirui edited this page Sep 20, 2020 · 3 revisions

复杂的状态机编写起来对程序员要求高

2007年,F#语言,把函数当变量来处理,函数块、程序块可赋值,可做异步编程

增加类型支持编程

底下的Runtime来支撑

kernel需要一定配合,Kernel abstraction 屏蔽kernel差异性

NodeJS V8编译器、运行时,基于此,做了扩展

Go语言,每个thread有自己的栈

回调函数越多,memory使用越大,call back很难debug,状态的保存与恢复需要管理

promise:链式编程方式

以同步的方式实现异步编程

OS需要提交事件,事件一旦ready,就会通知task

Epoll,高性能主流异步事件处理机制,只有三个函数

epoll_create():指定需要监听的IO操作

epoll_ctl():进一步设置,读监听,还是写监听

epoll_wait():事件ready了马上返回

21.2 Futures in Rust

异步编程的一种类型

Future的设计目标:零成本异步编程

调用IO时,系统调用会立即返回,然后可以继续进行其他工作

IO完成时,回到调用该异步IO暂停的那个任务线上继续执行

一种通过对异步IO的良好抽象形成的基于库的解决方案,不是语言的一部分,也不是每个程序附带的运行时的一部分,只是可选的并按需使用的库

零成本抽象:不给不使用该功能的用户增加成本;使用该功能时,它的速度不会比不使用它的速度慢

Async Architecture of rust

三部分:底层kernel IO tasks mio(kernel的抽象层,相当于中断机制,不断地产生事件,来触发等待状态的thread变成ready态,操作系统可以对thread进行进一步调度执行了) tokio(runtime,是一个special 异步的runtime,有两部分,一部分Reactor响应机制,另一部分Executor执行机制,executor调Future把数据给取回来) Future相当于thread,task任务,

Future是语言级、编译器要支持的内容,tokio、mio是runtime要支持的内容,kernel io tasks是OS要支持的内容

future最终会形成task,task会有不同的状态,如果是就绪状态的话,executor就可以执行它,如果task需要的内容没有来,pending挂起状态,executor把它放在挂起的队列里面就OK了,task会被外面的事件,OS的IO事件,通过reactor来发出一个waker,传给waker,通过waker来唤醒task,executor就会知道这个时候task已经变成ready了,executor会通过poll函数去调task

executor、reactor、waker三者的组合形成了整个architecture

future有两类:leaf futures(一个具体的IO请求,会直接向kernel发出调用) & Non-leaf futures(由一系列leaf和non-leaf组成的更大的future)

Rust有特定的库来支持:async-std、Tokio

common interface表示操作,但是没有实现,实现放在库中,灵活地把语言和库做了区分

库分了两类,标准库,具体库

21.3 Generators and async/await

生成器和异步执行

异步执行需要走走停停,程序中进行标注,编译器做更多的事

自引用数据结构和绑定

生成器:

一个进程里面并发,Rust里面给出支持

Stackful coroutines(green threads)

Using combinators 组合器

Stackless coroutines(generators)生成器,栈去掉,仍然能在里面切换执行

如何把一个异步函数,看上去像一个函数的执行

在Rust中,异步实现为一个生成器,生成器又实现为一个状态机,一个异步函数走一段停一段,停的过程,只要有停的地方,和有可能停的地方,连续的地方说成一个状态,停下来之后,恢复之后变成下一个状态,将一个函数切成若干段,每一段是可以连续执行的一个段落,切成一个状态机,在状态机之间完成一些转换,转换是由编译器来完成,从而在每一个状态变化的时候,我能知道我下一步转到哪去

转换成状态机,和状态机变迁,靠编译器来执行状态机的状态就OK,维护状态机的状态

21.4 自引用结构数据绑定

悬空指针问题

数据换地了,再引用就不对了

1、指针移动的时候,每动一次,数据就更新一遍;代价太大,Rust不采用

2、存时存起头的内容,偏移不变,起头变,不在编译时更新,在运行时更新,Rust不采用

3、Rust中禁止移动这种数据结构

在数据结构中加Pin,数据结构不允许用,由开发者来指定,开销就变小了

对一个指针的封装

好处:约定引用不会变,不会出现悬空指针

数据结构放在堆里面,在堆栈中生命周期与函数生命周期相关,所以Future中Pin数据结构放在堆里面,相对稳定一些

Clone this wiki locally