-
实现并发:开多进程和开多线程
-
若只有一个线程,肯定无法实现并行,但是否可以实现并发?
并发的实现手段:切换+保存状态,原来是由OS管控,没有I/O时操作系统也可能切换任务,因为要统筹全局。但是真正能提高效率的是有I/O的时候切换。 -
在自己的线程中,有五个任务,进行切换+保存状态,也叫并发,但是是由程序员控制,OS管不着。即,单线程下实现并发,成为协程。
-
协程不存在,只是人们规定的一种概念。
-
协程对提升效率是否有意义?
没有遇到I/O,单纯的切换反而会降低效率
应该遇到I/O才切换,yield和send无法实现 -
原先多线程,每个线程互不干涉,每个线程遇到I/O阻塞就得等待,多线程只是实现了任务切换,并没有解决I/O问题
-
在协程中,可以实现比如五个任务,一个任务遇到I/O需要等待,就切换到另外一个任务,这样针对整个线程阻塞的时间就降低了,CPU给每个线程的运行时间是有限的,但是另外两个就绪和阻塞时间是此消彼长的,降低了阻塞时间,就绪时间就增加了,从而增大了可以再次抢到CPU的机会,提高了效率。并且单线程内的切换是代码级的,CPU管不着,所以切换速度比较快。且操作系统看不到一个线程中的任务切换。
-
遇到I/O才切换的协程才有提高效率的意义,不遇到I/O就切换,也称作协程。单线程内实现并发,最大限度利用CPU
-
协程的本质是单线程下,无法利用多核,可以是一个程序开启多个进程,每个进程内开启多个线程,单个线程开启协程。协程一旦出现阻塞会阻塞整个线程。
-
Greenlet比yield好一些,可以简单的实现多任务直接切换,但是仍然知识纯粹切换,遇到I/O仍然不能实现切换,没有解决遇到I/O自动切换来提升效率的问题。
-
Gevent
假如有20个任务,通常会既有计算任务,也有I/O任务,完全可以在任务一遇到阻塞时利用阻塞的时间去执行任务2.。。。如此提高效率
Gevent=Greenlet+检测I/O遇到I/O就切换
Greenlet9(无法实现遇到I/O切换)
from greenlet import greenlet
#模拟多个任务,吃一口饭,玩一次手机
def eat(name):
print('%s eat 1' %name)
g2.switch(name)
print('%s eat 2' %name)
g2.switch(name)
def play(name):
print('%s play 1' %name)
g1.switch(name)
print('%s play 2' %name)
g1= greenlet(eat) #在第一次启动任务时才传参数
g2= greenlet(play)
g1.switch('egon') #启动任务,传参
结果:
egon eat 1
egon play 1
egon eat 2
egon play 2
gevent
#实现单线程多任务遇到I/O才切
#模拟多个任务,吃一口饭,玩一次手机
#实用gevent管理这两个任务
import gevent
def eat(name):
print('%s eat 1' %name)
gevent.sleep(3) #相当于time.sleep(3) ,模拟I/O 操作 #单纯的写,gevent只能识别自己的I/O识别操作
print('%s eat 2' %name)
def play(name):
print('%s play 1' %name)
gevent.sleep(2)
print('%s play 2' %name)
g1=gevent.spawn(eat,'egon')
g2=gevent.spawn(play,'egon') #参数可不同
#gevent.sleep(10) #保证线程两个任务能够有时间运行起来,避免线程开启任务就立刻死掉,但是不知道应该睡几秒
g1.join()
g2.join() #保证该线程等着上边两个任务运行完才死掉 这两句等价于 gevent.joinall([g1,g2])
#这两个是异步调用
结果:
egon eat 1
egon play 1
egon play 2
egon eat 2
#大概花了三秒,相比于串行的五秒提高了效率
但上边仅是gevent.sleep(5)自己模拟的I/O,可以识别,但没有什么用处,使用time.sleep(3)就无法识别了,一直在那等着
要想gevent能够识别所有的I/O操作,不局限于自己的
加上一个补丁
from gevent import monkey
monkey.patch_all() #保证下边所有I/O都能被识别,打补丁
import gevent
import time
def eat(name):
print('%s eat 1' %name)
time.sleep(3) #模拟I/O 操作 ,属于其他I/O类
print('%s eat 2' %name)
def play(name):
print('%s play 1' %name)
time.sleep(2)
print('%s play 2' %name)
g1=gevent.spawn(eat,'egon')
g2=gevent.spawn(play,'egon') #参数可不同
g1.join()
g2.join()
结果:
egon eat 1
egon play 1
egon play 2
egon eat 2