协程Coroutine
协程的目的
要在单线程下实现并发,在应用程序里控制多个任务 切换 + 保存(本质)
并发: 指的是多个任务同时发生,看似好像是同时进行,其实是切换进行
并行: 指的是多个任务真正的同时进行
并发 = 切换 + 保存
优点:
应用程序(自己操作)级别的切换速度要快于操作系统的切换速度
缺点:
多个任务一旦有一个阻塞没有切,整个线程都阻塞,该线程内的其他的任务都不能执行.
一旦引入协程,就需要检测单线程下的所有IO,实现遇到IO就切换
因为有一个任务阻塞了,整个线程都阻塞了,即便是纯计算也要阻塞.
总结协程特点:
- 必须在只有一个单线程里实现并发
- 修改共享数据不需要加锁
- 用户程序里自己保存多个控制流的上下文栈
- 一个协程遇到IO操作自动切换到其它协程(如何实现检测IO? yield,greenlet都无法实现, 就用到了gevent模块)
进程的流程:
线程的流程:
协程的流程:
Greenlet
"""
使用greenlet模块可以简单完成多个任务之间的切换(使用yield生成器的方式过于麻烦)
"""
# greenlet(run=None, parent=None): 创建一个greenlet实例
# g.parent: 每个协程都有一个父协程,当前协程结束后会回到父协程中执行,该属性默认是创建该协程的协程
# g.run : 该属性是协程实际运行的代码,run方法结束了,那么该协程也结束了
# g.switch(*args, **kwargs): 切换到g 协程
# g.throw() :切换到g协程, 接着抛出一个异常
from greenlet import greenlet
def eat(name):
print('%s est 1' % name)
g2.switch('kp')
print('%s eat 2' % name)
g2.switch()
def play(name):
print('%s play 1' % name)
g1.switch()
print('%s play 2' % name)
g1 = greenlet(eat)
g2 = greenlet(play)
g1.switch('kopa')
# kopa est 1
# kp play 1
# kopa eat 2
# kp play 2
Gevent介绍
Gevent是一种基于协程的Python网络库, 用到Greenlet提供的,封装了libevent事件循环的高层同步API. 用同步的方式写异步IO的代码.
使用Gevent的新能比一般的线程高, 但是也有坑:
1, Monkey-patching, 称之为猴子补丁, 因为如果使用了这个补丁,Gevent直接修改标准库里面大部分的阻塞式系统调用, 包括socket,threading和select等模块, 而变成协作式运行. (可能导入补丁会有其他问题)
2, 第三方库支持
"""
g1 = gevent.spwan(func,*args, **kwargs) :
第一个参数是函数名,如eat,后面可以跟参数,参数是传给eat的
g.join() : 等待g 结束
gevent.joinall([g1, g2]) : 等待g1, g2 结束
g.value : 拿到返回值
"""
# 猴子补丁放在要补丁的前面(可以看成放在最前面)
from gevent import monkey
monkey.patch_all()
import gevent
import time
def eat():
print('eat food 1')
time.sleep(1)
print('eat food 2')
def play():
print('play 1')
time.sleep(1)
print('play 2')
g1 = gevent.spawn(eat)
g2 = gevent.spawn(play)
gevent.joinall([g1, g2])
print('zhu')
# eat food 1
# play 1
# eat food 2
# play 2
# zhu