例子1: follow.py 可以使用生成器完成 tail -f 的功能,也就是跟踪输出的功能。

例子2: 生成器用作程序管道(类似unix pipe)

pipeline.py

理解pipeline.py
在pipeline中,follow函数和grep函数相当于程序链,这样就能链式处理程序。
Yield作为表达【我们开始说协程了~】:
grep.py

yield最重要的问题在于yield的值是多少。
yield的值需要使用coroutine协程这个概念 相对于仅仅生成值,函数可以动态处理传送进去的值,而最后值通过yield返回。
协程的执行:
协程的执行和生成器的执行很相似。 当你初始化一个协程,不会返回任何东西。 协程只能响应run和send函数。 协程的执行依赖run和send函数。 如果你缺志同道合的朋友!缺学习Python的氛围!缺入门资料和视频!缺书籍的PDF!缺遇到问题没人解答?那就加这个群:103456743 你想要一起学习的朋友?资料免费提供的学习环境?好比图书馆!不收你一分钱,只为提供一个良好的交流平台!编程贵在多交流!
协程启动:
所有的协程都需要调用.next( )函数。 调用的next( )函数将要执行到第一个yield表达式的位置。 在yield表达式的位置上,很容易去执行就可以。 协程使用next()启动。
使用协程的修饰器:
由【协程启动】中我们知道,启动一个协程需要记得调用next( )来开始协程,而这个启动器容易忘记使用。 使用修饰器包一层,来让我们启动协程。 【以后所有的协程器都会先有@coroutine

使用GeneratorExit这个异常类型
抛出一个异常:
在一个协程中,可以抛出一个异常



第三部分,管道过滤器:
叫过滤器其实并不贴切,应该叫中间人Intermediate:其两端都是send()函数。

(协程的中间层) 典型的中间层如下:

协程和生成器的对比

不同处:生成器使用了迭代器拉取数据,协程使用send()压入数据。
变得多分支:(上一个协程发送数据去多个下一段协程)
图示:

使用协程,你可以发送数据 给 多个 协程过滤器/协程终了。但是请注意,协程源只是用来传递数据的,过多的在协程源中传递数据是令人困惑并且复杂的。
一个例子

从文章中分别打印出含有’python‘ ’ply‘ ’swig‘ 关键字的句子。使用了一个协程队列向所有printer协程 送出 接收到的数据。 图示:

或者这样Hook them up:

第三部分:协程,事件分发
事件处理
协程可以用在写各种各样处理事件流的组件。
介绍一个例子【这个例子会贯穿这个第三部分始终】要求做一个实时的公交车GPS位置监控。编写程序的主要目的是处理一份文件。传统上,使用SAX进行处理。【SAX处理可以减少内存空间的使用,但SAX事件驱动的特性会让它笨重和低效】。
把SAX和协程组合在一起
我们可以使用协程分发SAX事件,比如:

【最终的组合】
比如,把xml改成json最后从中筛选的出固定信息. buses.py

协程的一个有趣的事情是,您可以将初始数据源推送到低级别的语言,而不需要重写所有处理阶段。比如,PPT 中69-73页介绍的,可以通过协程和低级别的语言进行联动,从而达成非常好的优化效果。如Expat模块或者cxmlparse模块。 ps: ElementTree具有快速的递增xml句法分析
第四部分:从数据处理到并发编程
复习一下上面学的特点:
协程有以下特点。
协程和生成器非常像。
我们可以用协程,去组合各种简单的小组件。
我们可以使用创建进程管道,数据流图的方法去处理数据。
你可以使用伴有复杂数据处理代码的协程。
一个相似的主题:
我们往协程内传送数据,向线程内传送数据,也向进程内传送数据。那么,协程自然很容易和线程和分布式系统联系起来。
基础的并发:
我们可以通过添加一个额外的层,从而封装协程进入线程或者子进程。这描绘了几个基本的概念。

目标!协程+线程【没有蛀牙。
下面看一个线程的例子。 cothread.py

例子解析:第一部分:先新建一个队列。然后定义一个永久循环的线程;这个线程可以将其中的元素拉出消息队列,然后发送到目标里面。第二部分:接受上面送来的元素,并通过队列,将他们传送进线程里面。其中用到了GeneratorExit ,使得线程可以正确的关闭。
Hook up:cothread.py


但是:添加线程让这个例子慢了50%
目标!协程+子进程
我们知道,进程之间是不共享系统资源的,所以要进行两个子进程之间的通信,我们需要通过一个文件桥接两个协程。


程序通过sendto()和recvfrom()传递文件。
和环境结合的协程:
使用协程,我们可以从一个任务的执行环境中剥离出他的实现。并且,协程就是那个实现。执行环境是你选择的线程,子进程,网络等。
需要注意的警告 :
创建大量的协同程序,线程和进程可能是创建 不可维护 应用程序的一个好方法,并且会减慢你程序的速度。需要学习哪些是良好的使用协程的习惯。
在协程里send()方法需要被适当的同步。
如果你对已经正在执行了的协程使用send()方法,那么你的程序会发生崩溃。如:多个线程发送数据进入同一个协程。
同样的不能创造循环的协程:

堆栈发送正在构建一种调用堆栈(send()函数不返回,直到目标产生)。
如果调用一个正在发送进程的协程,将会抛出一个错误。
send() 函数不会挂起任何一个协程的执行。
第五部分:任务一样的协程
Task的概念
在并发编程中,通常将问题细分为“任务”。 “任务”有下面几个经典的特点: * 拥有独立的控制流。 * 拥有内在的状态。 * 可以被安排规划/挂起/恢复。 * 可与其他的任务通信。 协程也是任务的一种。
协程是任务的一种:
下面的部分 来告诉你协程有他自己的控制流,这里 if 的控制就是控制流。


需要解决的问题(还在复习微嵌知识)
CPU执行的是应用程序,而不是你的操作系统,那 没有被CPU执行的操作系统 是怎么控制 正在运行的应用程序 中断的呢。
中断(interrupts)和陷阱(Traps)
操作系统只能通过两个机制去获得对应用程序的控制:中断和陷阱。 * 中断:和硬件有关的balabala。 * 陷阱:一个软件发出的信号。 在两种状况下,CPU都会挂起正在做的,然后执行OS的代码(这个时候,OS的代码成功插入了应用程序的执行),此时,OS来切换了程序。
中断的底层实现(略…码字员微嵌只有70分��♀️)
中断的高级表现:
* 中断(Traps)使得OS的代码可以实现。* 在程序运行遇到中断(Traps)时,OS强制在CPU上停止你的程序。* 程序挂起,然后OS运行。
表现如下图:

每次中断(Traps)程序都会执行另一个不同的任务。
任务调度(非常简单):
为了执行很多任务,添加一簇任务队列。

启示(很重要):
BB了这么多微嵌的内容,得到的是什么结论呢。类比任务调度,协程中yield声明可以理解为中断(Traps)。当一个生成器函数碰到了yield声明,那函数将立即挂起。而执行被传给生成器函数运行的任何代码。如果你把yield声明看成了一个中断,那么你就可以组件一个多任务执行的操作系统了。
第七部分:让我们建一个操作系统。【起飞了,请握好扶手
目标:满足以下条件建成一个操作系统。
1. 用纯python语句。2. 不用线程。3. 不用子进程。4. 使用生成器和协程器。
我们用python去构建操作系统的一些动机:
* 尤其在存在线程锁(GIL)的条件下,在线程间切换会变得非常重要。我要高并发!* 不阻塞和异步I/O。我要高并发!* 在实战中可能会遇到:服务器要同时处理上千条客户端的连接。我要高并发!* 大量的工作 致力于实现 事件驱动 或者说 响应式模型。我要组件化!* 综上,python构建操作系统,有利于了解现在高并发,组件化的趋势。
第一步:定义任务
定义一个任务类:任务像一个协程的壳,协程函数传入target;任务类仅仅有一个run()函数。 pyos1.py

在foo中,yield就像中断(Traps)一样,每次执行run(),任务就会执行到下一个yield(一个中断)。
第二步:构建调度者
下面是调度者类,两个属性分别是Task队列和task_id与Task类对应的map。schedule()向队列里面添加Task。new()用来初始化目标函数(协程函数),将目标函数包装在Task,进而装入Scheduler。最后mainloop会从队列里面拉出task然后执行到task的target函数的yield为止,执行完以后再把task放回队列。这样下一次会从下一个yield开始执行。 pyos2.py


第三步:确定任务的停止条件
如果,target函数里面不是死循环,那么上面的代码就会出错。所以我们对Scheduler做改进。添加一个从任务队列中删除的操作,和对于StopIteration的验证。 【对scheduler做改进的原因是任务的性质:可以被安排规划/挂起/恢复。】

第四步:添加系统调用基类。
在OS中,中断是应用程序请求系统服务的方式。在我们的代码中,OS是调度者(scheduler),而中断是yield。为了请求调度者服务,任务需要带值使用yield声明。 pyos4.py

代码解析: 1. 如果taskmap里面存在task,就从ready队列里面拿任务出来,如果没有就结束mainloop。 2. 【就是传说中的系统调运部分】ready队列里面的task被拿出来以后,执行task,返回一个result对象,并初始化这个result对象。如果队列里面的task要停止迭代了(终止yield这个过程)就从队列里删除这个任务。 3. 最后再通过schedule函数把执行后的task放回队列里面。 4. 系统调用基类,之后所有的系统调用都要从这个基类继承。
第4.5步:添加第一个系统调用
这个系统调用想返回任务的id。 Task的sendval属性就像一个系统调用的返回值。当task重新运行的是后,sendval将会传入这个系统调用。 pyos4.py

理解这段代码的前提: (非常重要) 1. send()函数有返回值的,返回值是yield表达式右边的值。在本段代码中,result的返回值是yield GetTid()的GetTid的实例或者是yield后面的。 2. 执行send(sendval)以后,sendval被传入了yield表达式。并赋给了mytid,返回GetTid()给ruselt。
执行顺序: 先创建一个调度者(Scheduler),然后在调度者里面添加两个协程函数:foo(), bar(),最后触发mainloop进行协程的调度执行。
系统调用原理: 系统调用是基于系统调用类实现的,如GetTid类,其目的是传出自己的tid。传出自己的tid之后,再将task放回队列。
第五步:任务管理
上面我们搞定了一个GetTid系统调用。我们现在搞定更多的系统调用: * 创建一个新的任务。 * 杀掉一个已经存在的任务。 * 等待一个任务结束。 这些细小的相同的操作会与线程,进程配合。
1. *创建一个新的系统调用*:通过系统调用加入一个task。




网络服务器的搭建:
现在已经完成了: * 多任务。 * 开启新的进程。 * 进行新任务的管理。 这些特点都非常符合一个web服务器的各种特点。下面做一个Echo Server的尝试。

但问题是这个网络服务器是I / O阻塞的。整个python的解释器需要挂起,一直到I/O操作结束。
非阻塞的I/O
先额外介绍一个叫Select的模块。select模块可以用来监视一组socket链接的活跃状态。用法如下:


源码解析:__init__里面的是两个字典。用来存储阻塞的IO的任务。waitforread()和waitforwrite()将需要等待写入和等待读取的task放在dict里面。这里的iopoll():使用select()去决定使用哪个文件描述器,并且能够不阻塞任意一个和I/O才做有关系的任务。poll这个东西也可以放在mainloop里面,但是这样会带来线性的开销增长。 详情请见: Python Select 解析 - 金角大王 - 博客园
添加新的系统调用:

更多请见开头那个连接里面的代码:pyos8.py
这样我们就完成了一个多任务处理的OS。这个OS可以并发执行,可以创建、销毁、等待任务。任务可以进行I/O操作。并且最后我们实现了并发服务器。
第八部分:协程栈的一些问题的研究。
我们可能在使用yield的时候会遇到一些问题:

让我们来看看这个叫蹦床的奇淫巧技。
代码:trampoline.py

整个控制流如下:

我们看到,上图中左侧为统一的scheduler,如果我们想调用一个子线程,我们都用通过上面的scheduler进行调度。

千万不要一个函数里面包含两个或多个以上的功能,比如函数是generator就是generator,是一个coroutine就是一个coroutin。
谢谢阅读!

如有侵权请联系小编删除哦!
相关资讯
最新热门应用
非小号交易平台官网安卓版
其它软件292.97MB
下载
币交易所地址
其它软件274.98M
下载
iotx交易所app
其它软件14.54 MB
下载
zt交易所安卓最新版
其它软件273.2 MB
下载
币拓交易所bittok
其它软件288.1 MB
下载
u币交易所平台app
其它软件292.97MB
下载
热币全球交易所app官网版
其它软件287.27 MB
下载
多比交易平台app
其它软件28.28MB
下载
币赢交易所app官网安卓版
其它软件14.78MB
下载
toncoin币交易所安卓版
其它软件48MB
下载