一、协程
协程在python语言中的地位是不可撼动的。协程弥补了线程给程序带来的遗憾
协程的使用场景:爬虫,访问网页常用的方式,极大程度的节省时间
进程:计算机中最小的资源分配单位
线程:计算机中能被CPU执行的最小单位
协程也叫纤程,比线程还小的单位,cpu看不见协程,协程仍然是一条线程,N个任务交替轮流执行。
本质:一条线程在多个任务之间来回切换,一条线程做多个任务
协程切换是需要消耗时间的,对于cpu、操作系统来说,协程是不存在的,因为它们只能看到线程的概念
协程的好处:
更好的利用协程,一个线程的执行明确的切分开,两个任务帮助你记住哪个任务执行到哪个位置上,并且实现安全的切换
一个任务不得不陷入阻塞,在这个任务阻塞的过程中切换到另一个任务继续执行
若程序只要任务还需要执行,当前线程永远不阻塞
让一个线程模拟有更多的任务需要执行,给操作系统造成假象伪装成高计算型的程序,cpu分配更多的时间片给线程
利用协程在多个任务陷入阻塞的时候进行切换来保证一个线程在处理多个任务的时候总是忙碌的,这样能够更加充分的利用CPU,抢占更多的时间片
无论是进程还是线程都是由操作系统来切换的,开启过多的进程或者线程会给操作系统增加负担,如果使用协程,那么协程在程序之间的切换操作系统感知不到,无论开启多个协程仍以为是一个线程,操作系统的调度就不会有压力。
协程的本质就是一条线程,完全不会发生数据不安全的问题,它是以python的代码来切换,不是根据CPU指令来切换
二、协程的创建(greenlet)
from greenlet import greenlet 协程的底层的模块,用于切换
方法:
switch() 手动切换方法,遇到阻塞切换到其他进程
g1 = greenlet(func) 初始化对象
from greenlet import greenlet
import time
def eat():
print('eating start')
g2.switch() # 手动切换,遇到阻塞切换到其他进程
time.sleep(2)
print('eating end')
def play():
print('playing start')
time.sleep(2)
print('playing end')
g1.switch() # 手动切换,遇到阻塞切换到其他进程
g1 = greenlet(eat) # 初始化协程对象
g2 = greenlet(play)
g1.switch()
三、协程的创建(gevent)
import gevent 可以直接使用,在greenlet模块的基础上做封装,gevent能提供更全面的功能,遇到IO切换会主动切换,规避IO的操作
方法:
gevent.sleep(1) 睡眠,阻塞事件
g1 = gevent.spawn(func) 产生了协程任务,自动的检测阻塞事件,遇见阻塞会进行切换,但有些阻塞不认识
g1.join() 阻塞直到某个任务执行完毕
gevent.joinall(gevent的所有对象列表) 阻塞直到所有任务执行完毕
g1.value value是属性,获取协程的返回值
from gevent import monkey;monkey.patch_all() # 下面导入模块,就会把模块导入记录下来
from gevent import monkey;monkey.patch_all() # 下面导入模块,就会把模块导入记录下来
import time
import gevent
def eat():
print('eating start')
time.sleep(2)
print('eating end')
def play():
print('playing start')
time.sleep(2)
print('playing end')
g1 = gevent.spawn(eat) # 自动检测阻塞事件,遇到阻塞进行切换
g2 = gevent.spawn(play) # 自动检测阻塞事件,遇到阻塞进行切换
g1.join() # 阻塞直到任务完成
g2.join() # 阻塞直到任务完成
'''
eating start
playing start
eating end
playing end
'''
获取返回值
from gevent import monkey;monkey.patch_all() # 下面导入模块,就会把模块导入记录下来
import time
import gevent
def eat():
print('eating start')
time.sleep(2)
print('eating end')
return 'eat function'
def play():
print('playing start')
time.sleep(2)
print('playing end')
return 'play function'
g1 = gevent.spawn(eat) # 自动检测阻塞事件,遇到阻塞进行切换
g2 = gevent.spawn(play) # 自动检测阻塞事件,遇到阻塞进行切换
gevent.joinall([g1,g2]) # 阻塞,直到全部任务执行完毕
print(g1.value) # 打印协程的返回值
print(g2.value) # 打印协程的返回值
'''
eating start
playing start
eating end
playing end
eat function
play function
'''