python 协程
协程
-
单线程下实现并发
-
是一种轻量级的线程, 由程序自己控制调度的 线程是内核级的, 协程是程序级别的
-
优点:
协程的切换开销小,操作系统完全感知不到, 单线程内就可以实现并发效果,最大限度的 利用 CPU
-
协程遇到 IO 就自动切换到其他协程 检测 IO yield greenlet 无法实现
用 gevent 模块(select 机制) 实现 IO 的切换
-
并发:
伪并行,遇到IO就切换,单核下多个任务之间切换执行,给你的效果就是貌似你的几个程序在同时执行.提高效率
任务切换 + 保存状态 -
并行:
多核cpu,真正的同时执行
-
串行:
一个任务执行完在执行另外一个任务
-
首先,给出“进程、线程和协程”的特点:
进程:拥有自己独立的堆和栈,既不共享堆,也不共享栈,进程由操作系统调度;
线程:拥有自己独立的栈和共享的堆,共享堆,不共享栈,标准线程由操作系统调度;
协程:拥有自己独立的栈和共享的堆,共享堆,不共享栈,协程由程序员在协程的代码里显示调度。import time '''串行 用时''' def func1(): time.sleep(1) c = 0 for i in range(1000): c = i ** i print("func1>>", c) def func2(): time.sleep(2) c = 0 for i in range(1000): c = i ** i print("func2>>", c) if __name__ == '__main__': s_t = time.time() func1() func2() print(time.time() - s_t) # 3.0608773231506348
基于yield并发执行
'''基于yield并发执行,多任务之间来回切换,这就是个简单的协程的体现,但是他能够节省I/O时间吗?不能''' def func1(): while 1: time.sleep(1) x = yield print("接收了%s 个任务" % x) def func2(): f = func1() # 找到 第一个 yield next(f) for i in range(1, 11): f.send(i) print("发送了 %s 个任务" % i) s_t = time.time() # 基于yield保存状态,实现两个任务直接来回切换,即并发的效果 # PS: 如果每个任务中都加上打印,那么明显地看到两个任务的打印是你一次我一次,即并发执行的. func2() # 我在当前线程中只执行了这个函数,但是通过这个函数里面的send切换了另外一个任务 print(time.time() - s_t) # 11.003206253051758 # 串行的执行 方式 # func1() # func2() # print(time.time() - s_t) # 11.00475525856018
greenlet 模块
- 真正的协程模块就是使用 greenlet 完成的切换
import time
from greenlet import greenlet
def eat(name):
print('%s eat 1' % name) # 2
time.sleep(3)
g2.switch('taibai') # 3
print('%s eat 2' % name) # 6
g2.switch() # 7
def play(name):
print('%s play 1' % name) # 4
time.sleep(3)
g1.switch() # 5
print('%s play 2' % name) # 8
g1 = greenlet(eat)
g2 = greenlet(play)
g1.switch('taibai') # 可以在第一次switch时传入参数,以后都不需要
gevent 使用
- 作用: 任务切换 + 保存状态 实现了 IO 自动切换
import gevent
from gevent import monkey
monkey.patch_all() # 补丁 识别所有的 IO 阻塞 time.sleep() 就可以用了
import time
def func1(name):
print(name, 111111111)
time.sleep(1)
# gevent.sleep(1)
print(name, 2222222222)
return 123
def func2(name):
print(name, 111)
# gevent.sleep(1) # time.sleep() 识别不到
time.sleep(1)
print(name, 222)
g1 = gevent.spawn(func1, "egen") # 异步执行这个func1任务,后面egon就是给他传的参数
print(g1.value) # 取出返回值的 结果
g2 = gevent.spawn(func2, "two")
# 此 方法 不用 单独 指定 join()
gevent.joinall([g1, g2])
print("aaaaaa")
################################################################
def task(pid):
"""
Some non-deterministic task
"""
time.sleep(0.5)
print('Task %s done' % pid)
def synchronous():
for i in range(10):
task(i)
def asynchronous():
g_l = [gevent.spawn(task, i) for i in range(10)]
gevent.joinall(g_l)
if __name__ == '__main__':
print('Synchronous:') # 同步的 并发 执行
synchronous()
print('Asynchronous:') # 异步的 并行 执行
asynchronous()
# 上面程序的重要部分是将task函数封装到Greenlet内部线程的gevent.spawn。 初始化的greenlet列表存放在数组threads中,此数组被传给gevent.joinall 函数,后者阻塞当前流程,并执行所有给定的greenlet。执行流程只会在 所有greenlet执行完后才会继续向下走。