一、协程概述
1、什么是协程?
协程也被称为“微线程”,在一个线程中规定某个代码块的执行顺序。线程和进程的操作是由程序触发系统接口,最后的执行者是系统;协程的操作则是程序员。
2、为什么会有协程?
对于多线程应用,CPU通过切片的方式来切换线程间的执行,线程切换时需要耗时(保存状态,下次继续)。协程,则只使用一个线程,在一个线程中规定某个代码块执行顺序。这样更加节约切换的时间。
3、协程的特点?
- 不需要多线程的锁机制
因为只有一个线程,不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。
- 可以利用多核特点
通过多进程 + 协程既可以利用多核又可以发挥协程的高效率。
- 从技术的角度来说,“协程就是你可以暂停执行的函数”
类似于python中的生成器
4、协程的应用场景
当程序中存在大量不需要CPU的操作时(IO),适用于协程。
二、greenlet、gevent模块
(一)greenlet
上面说了从技术层面上说“协程就是你可以暂停执行的函数”,相当于一个生成器,greenlet是一个用C实现的协程模块:
from greenlet import greenlet def test1(): print(9) gr2.switch() #手动切换执行test2 print(22) gr2.switch() #手动切换执行test2 def test2(): print(2) gr1.switch() #手动切换执行test1 print(36) gr1 = greenlet(test1) gr2 = greenlet(test2) gr1.switch() #开始执行test1 """ 输出: 9 2 22 36 """
可以看出来greenlet需要手动控制来执行代码块的。
(二)gevent
这个模块比上一个模块智能许多,它不需要手动控制代码块的执行顺序,只是如果协程碰到I/O操作,就会自动切换。
import gevent def test1(): print(9) #第2步 gevent.sleep(1) #第3步 print(22) #第7步 def test2(): print(2) #第4步 gevent.sleep(0.3) #第5步 print(36) #第6步 gevent.joinall([ gevent.spawn(test1), #第1步 gevent.spawn(test2), ]) """ 输出: 9 2 36 22 """
当然,还可以使用gevent模块进行爬虫:
import gevent import requests from gevent import monkey monkey.patch_all() def task(method,url,req_kwargs): response=requests.request(method=method,url=url,**req_kwargs) print(response.url,response.content) #发送请求 gevent.joinall( { gevent.spawn(task,method='get',url='http://baidu.com/',req_kwargs={}), gevent.spawn(task, method='get', url='https://www.tudou.com/', req_kwargs={}) } )
import gevent import requests from gevent.pool import Pool from gevent import monkey monkey.patch_all() def task(method,url,req_kwargs): response=requests.request(method=method,url=url,**req_kwargs) print(response.url,response.content) #发送请求 pool=Pool(5) gevent.joinall( { pool.spawn(task,method='get',url='http://baidu.com/',req_kwargs={}), pool.spawn(task, method='get', url='https://www.tudou.com/', req_kwargs={}) } )
上面使用的是利用gevent进行并发发送请求,requests爬取网页内容,而grequests模块将两者进行结合,既有并发的特性又有requests的特性。
#利用grequests进行并发 将requests和gevent进行封装 import grequests request_list=[ grequests.get('http://baidu.com/'), grequests.get('https://www.tudou.com/') ] response_list=grequests.map(request_list,size=5) print(response_list) #[<Response [200]>, <Response [200]>]