天天看点

Python协程实践

 协程简单来说就是一个更加轻量级的线程,并且不由操作系统内核管理,完全由程序所控制(在用户态执行)。协程在子程序内部是可中断的,然后转而执行其他子程序,在适当的时候返回过来继续执行。  协程的优势?(协程拥有自己的寄存器上下文和栈,调度切换时,寄存器上下文和栈保存到其他地方,在切换回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文非常快。)
1、协程中的yield通常出现在表达式的右边:
如果yield的右边没有表达式,默认产出的值是none,现在右边有表达式,所以返回的是data这个值。 2、协程可以从调用法接受数据,调用通过send(x)方式将数据提供给协程,同时send方法中包含next方法,所以程序会继续执行。 3、协程可以中断执行,去执行另外的协程。
代码:
代码详解: 程序开始执行,函数hello不会真的执行,而是返回一个生成器给a。 当调用到next()方法时,hello函数才开始真正执行,执行print方法,继续进入while循环; 程序遇到yield关键字,程序再次中断,此时执行到a.send("hello")时,程序会从yield关键字继续向下执行,然后又再次进入while循环,再次遇到yield关键字,程序再次中断;

额外

协程在运行过程中的四个状态: gen_create:等待开始执行 gen_running:解释器正在执行 gen_suspended:在yield表达式处暂停 gen_closed:执行结束
代码分析: 调用next(c)启动生成器; 消费者一旦生产东西,通过c.send切换到消费者consumer执行; consumer通过yield关键字获取到消息,在通过yield把结果执行; 生产者拿到消费者处理过的结果,继续生成下一条消息; 当跳出循环后,生产者不生产了,通过close关闭消费者,整个过程结束;
原理:gevent基于协程的python网络库,当一个greenlet遇到io操作(访问网络)自动切换到其他的greenlet等到io操作完成后,在适当的时候切换回来继续执行。换而言之就是greenlet通过帮我们自动切换协程,保证有greenlet在运行,而不是一直等待io操作。
由于切换时在发生io操作时自动完成,所以gevent需要修改python内置库,这里可以打上猴子补丁(用来在运行时动态修改已有的代码,而不需要原有的代码)monkey.patch_all
运行结果:
Python协程实践

结果:3个网络连接并发执行,但是结束的顺序不同。

原理:asyncio的编程模型就是一个消息循环,从asyncio模块中直接获取一个eventloop(事件循环)的应用,然后把需要执行的协程放入eventloop中执行,实现异步io。 经典代码:
代码解析: 首先获取一个eventloop 然后将这个hello的协程放进eventloop,运行eventloop,它会运行知道future被完成 hello协程内部执行await asyncio.sleep(1)模拟耗时1秒的io操作,在此期间,主线程并未等待,而是去执行eventloop中的其他线程,实现并发执行。

代码结果:

Python协程实践

异步爬虫实例:

创建一个事件循环,然后将任务放到时间循环中; run()方法中主要是创建任务,并发执行任务,返回读取到的网页内容; fetch()方法通过aiohttp发出指定的请求,以及返回 可等待对象;
Python协程实践

(结束输出网址和list中网址的顺序不同,证明协程中异步i/o操作)

asyncio实现类tcp、udp、ssl等协议,aiohttp则是基于asyncio实现的http框架,由此可以用来编写一个微型的http服务器。
创建一个事件循环,传入到init协程中; 创建application实例,然后添加路由处理指定的请求; 通过loop创建tcp服务,最后启动事件循环;
https://www.liaoxuefeng.com/wiki/1016959663602400/1017985577429536 https://docs.aiohttp.org/en/stable/web_quickstart.html https://docs.python.org/zh-cn/3.7/library/asyncio-task.html