Python爬虫--协程
2020/4/20 更新,根据给社团新生的讲课内容适当进行了补充
协程
- 协程(coroutine),又称微线程,纤程, 是一种用户级的轻量级的线程。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存在其他地方,在切换回来时,恢复先前保存的寄存器上下文和栈。因此协程可以保存上一次调用的状态,每次过程重入时,就相当于进入上一次调用的状态。在并发编程中,协程与线程类似,每个协程表示一个执行单元,有自己的本地数据,与其他协程共享全局数据和其他资源。
-
协程需要用户自己来编写调度逻辑,对于CPU来说,协程实际是单线程,所以CPU不考虑怎么去调度,切换上下文,这就省去了CPU切换的开销,所以协程在一定程度上又好于多线程。
-
Python使用yield提供了对协程的基本支持
-
第三方库gevent库是最好的选择,gevent提供了比较完善的协程支持。gevent是一个基于协程的Python的网络函数库,gevent对协程的支持本质是greenlet在实现切换工作。greenlet工作流程如下:假如进行访问网络的I/O操作时,出现阻塞,greenlet就显示切换到另一段没有被阻塞的代码段执行,直到原先的阻塞情况消失以后,再自动切换回原来的代码段继续执行。因此,greenlet是一种合理安排的串行方式。
-
I/O操作耗时,而使程序处于等待状态,有gevent帮我们切换,保证了总有greenlet在运行,而不是等待I/O。
例子1
1 | from gevent import monkey; monkey.patch_all() |
结果
1 | Visit ---> https://github.com/ |
- 以上主要使用了gevent中的spawn方法和joinall方法,spawn方法可以看做是用来形成协程,而joinall方法就是添加这些任务,而且启动运行,从结果来看,三个操作似乎是并发的,而且结束顺序不同,但实际只有一个线程。
补充关于 yield 的例子
1 | # -*- coding:utf-8 -*- |
- 当我们需要下一个 g 的时候,gen 才会继续执行,直到执行到 yield 语句后暂停。这里就可以很明显的猜到其实现就是基于协程的实现,每次执行到 yield 语句的时候保存现场然后切换出去(执行运行的代码的下一条语句),需要计算下一个值的时候恢复上次的计算状态然后接着计算。
优势
-
协程最大的优势就是协程极高的执行效率。因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。
-
第二大优势就是不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。