以前咱们学习了线程、进程的概念,了解了在操做系统中进程是资源分配的最小单位,线程是CPU调度的最小单位。按道理来讲咱们已经算是把CPU的利用率提升不少了。可是咱们知道不管是建立多进程仍是建立多线程来解决问题,都要消耗必定的时间来建立进程、建立线程、以及管理他们之间的切换。python
随着咱们对于效率的追求不断提升,基于单线程来实现并发又成为一个新的课题,即只用一个主线程(很明显可利用的cpu只有一个)状况下实现并发。这样就能够节省建立线进程所消耗的时间。编程
为此咱们须要先回顾下并发的本质:切换+保存状态。多线程
CPU正在运行一个任务,会在两种状况下切走去执行其余的任务(切换由操做系统强制控制),一种状况是该任务发生了阻塞,另一种状况是该任务计算的时间过长。并发
ps:在介绍进程理论时,说起进程的三种执行状态,而线程才是执行单位,因此也能够将上图理解为线程的三种状态。异步
一:其中第二种状况并不能提高效率,只是为了让cpu可以雨露均沾,实现看起来全部任务都被“同时”执行的效果,若是多个任务都是纯计算的,这种切换反而会下降效率。socket
为此咱们能够基于yield来验证。yield自己就是一种在单线程下能够保存任务运行状态的方法,咱们来简单复习一下:异步编程
协程:是单线程下的并发,又称微线程,纤程。英文名Coroutine。函数
一句话说明什么是协程:协程是一种用户态的轻量级线程,即协程是由用户程序本身控制调度的。学习
须要强调的是:spa
对比操做系统控制线程的切换,用户在单线程内控制协程的切换。
优势:
缺点:
总结协程特色:
import time def func1(): while True: 1000000+1 yield def func2(): g = func1() for i in range(100000000): i+1 next(g) start = time.time() func2() stop = time.time() print(stop - start) # 28.522686004638672 ### 对比经过yeild切换运行的时间反而比串行更消耗时间,这样实现的携程是没有意义的。 import time def func1(): for i in range(100000000): i+1 def func2(): for i in range(100000000): i+1 start = time.time() func1() func2() stop = time.time() print(stop - start) # 17.141255140304565
pip3 install gevent
Gevent 是一个第三方库,能够轻松经过gevent实现并发同步或异步编程,在gevent中用到的主要模式是Greenlet, 它是以C扩展模块形式接入Python的轻量级协程。 Greenlet所有运行在主程序操做系统进程的内部,但它们被协做式地调度。
g1=gevent.spawn(func,1,,2,3,x=4,y=5)建立一个协程对象g1,spawn括号内第一个参数是函数名,如eat,后面能够有多个参数,能够是位置实参或关键字实参,都是传给函数eat的
g2=gevent.spawn(func2)
g1.join() #等待g1结束
g2.join() #等待g2结束
g1.value#拿到func1的返回值
import gevent def eat(name): print('%s eat 1' %name) gevent.sleep(2) print('%s eat 2' %name) def play(name): print('%s play 1' %name) gevent.sleep(1) print('%s play 2' %name) g1=gevent.spawn(eat,'egon') g2=gevent.spawn(play,name='egon') g1.join() g2.join() #或者gevent.joinall([g1,g2]) print('主')
上例gevent.sleep(2)模拟的是gevent能够识别的io阻塞,
而time.sleep(2)或其余的阻塞,gevent是不能直接识别的须要用下面一行代码,打补丁,就能够识别了
from gevent import monkey;monkey.patch_all()必须放到被打补丁者的前面,如time,socket模块以前
或者咱们干脆记忆成:要用gevent,须要将from gevent import monkey;monkey.patch_all()放到文件的开头
from gevent import monkey monkey.patch_all() import gevent import time def eat(): print('eat food 1') time.sleep(2) print('eat food 2') def play(): print('play 1') time.sleep(1) print('play 2') start = time.time() g1 = gevent.spawn(eat) g2 = gevent.spawn(play) g1.join() g2.join() # gevent.joinall([g1,g2]) end = time.time() # 3.0165441036224365 # 若是打好了补丁 就能够识别非gevent.sleep阻塞进行切换 print(end-start)