一、协程概述
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]>]
