协程介绍
协程,又称微线程。英文名Coroutine。协程是Python语言中所特有的,在其他语言中没有。
协程是python中另外一种实现多任务的方式,比线程更小、占用更小执行单元(理解为需要的资源)。
在一个线程中的某个函数,可以在任何地方保存当前函数的一些临时变量等信息,然后切换到另外一个函数中执行。
注意不是通过调用函数的方式做到的,并且切换的次数以及什么时候再切换到原来的函数都由开发者自己决定。
在实现多任务时, 线程切换从系统层面来说,远不止保存和恢复CPU上下文那么简单。操作系统为了程序运行的高效性,每个线程都有自己缓存的数据,操作系统还会帮我们做这些数据的恢复操作。所以线程的切换非常耗性能。协程的切换只是单纯的操作CPU的上下文,所以一秒钟切换上百万次系统都抗的住。
gevent简介
一个基于greenlet的并发网络库。有了gevent后,不必向greenlet那样手动切换,而是当一个协程阻塞时,将自动切换到其他协程。
gevent常用方法
gevent.spawn() # 创建一个协程并启动
gevent.sleep() # 会阻塞切换到其他协程继续运行
gevent.joinall([]) # 会等待所有传入的协程运行(与启动不同)结束后退出
使用gevent实现协程
安装gevent
pip install gevent
示例1
import time
import requests
import gevent
from gevent import monkey
# monkey.patch_all() # 补丁(将python标准库都替换成非阻塞式的,使得DNS可以并发解析)
# monkey.patch_time() # 补丁(目前认为将python中的`time`标准库替换成非阻塞式的,使得DNS可以并发解析)
def func1():
print('func1 start')
gevent.sleep(5)
print('func1 stop')
def func2():
print('func2 start')
gevent.sleep(3)
print('func2 stop')
t1 = time.time()
gevent.joinall([
gevent.spawn(func1),
gevent.spawn(func2)
])
t2 = time.time()
print(t2 - t1)
def func3():
print('func3 start')
time.sleep(5)
print('func3 stop')
def func4():
print('func4 start')
time.sleep(3)
print('func4 stop')
t3 = time.time()
gevent.joinall([
gevent.spawn(func3),
gevent.spawn(func4)
])
t4 = time.time()
print(t4 - t3)
执行结果如下
func1 start
func2 start
func2 stop
func1 stop
5.078436851501465
func3 start
func3 stop
func4 start
func4 stop
8.023274660110474
# 可以看出gevent.sleep()与time.sleep()有较明显的区别;代码执行到gevent.sleep()时会切换协程,而time.sleep()则不会;
# 要解决上述问题,需要在代码开头引入monkey补丁(monkey.patch_all()或monkey.patch_time());
引入monkey补丁后,执行结果如下:
func1 start
func2 start
func2 stop
func1 stop
5.075600624084473
func3 start
func4 start
func4 stop
func3 stop
5.019229888916016