协程
协程一个简单实现
import time
def A():
while True:
print("----A---")
yield
time.sleep(0.5)
def B(c):
while True:
print("----B---")
c.next()
time.sleep(0.5)
if __name__=='__main__':
a = A()
B(a)
协程-greenlet版
为了更好使用协程来完成多任务,python中的greenlet模块对其封装,从而使得切换任务变的更加简单
安装方式
使用如下命令安装greenlet模块:
sudo pip install greenlet
from greenlet import greenlet import time def demo1(): while 1: print('---A---') gr2.switch() time.sleep(0.5) def demo2(): while 1: print('---B---') gr1.switch() time.sleep(0.5) gr1 = greenlet(demo1) gr2 = greenlet(demo2) gr1.switch()
gevent
greenlet已经实现了协程,但是这个还的人工切换,是不是觉得太麻烦了,不要捉急,python还有一个比greenlet更强大的并且能够自动切换任务的模块gevent
其原理是当一个greenlet遇到IO(指的是input output 输入输出,比如网络、文件操作等)操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。
由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有greenlet在运行,而不是等待IO
gevent切换执行
import gevent import time def f(n): for i in range(n): print(gevent.getcurrent(), i) gevent.sleep(0.5) # 注意这里的sleep为gevent的sleep函数 g1 = gevent.spawn(f, 5) g2 = gevent.spawn(f, 5) g3 = gevent.spawn(f, 5) g1.join() g2.join() g3.join()
gevent并发下载器
当然,实际代码里,我们不会用gevent.sleep()去切换协程,而是在执行到IO操作时,gevent自动切换,代码如下
1 import requests 2 import gevent 3 from gevent import monkey 4 5 monkey.patch_all() # 动态替换了涉及io操作的函数,,,当遇到阻塞时自动切换 6 7 def download(url, file): 8 response = requests.get(url) 9 with open(file, 'w') as f: 10 f.write(response.content.decode()) 11 # 打印url,,,对应的字节码数 12 print('url----{}, bytes----{}'.format(url, len(response.content.decode())), response.status_code) 13 14 15 # 全部阻塞 16 gevent.joinall([ 17 gevent.spawn(download, 'https://www.sina.com', 'sina.html'), 18 gevent.spawn(download, 'http://www.126.com', '126.html'), 19 gevent.spawn(download, 'https://www.baidu.com', 'baidu.html'), 20 21 ])
结果:
url----https://www.baidu.com, bytes----2349 200 url----http://www.126.com, bytes----105166 200 url----https://www.sina.com, bytes----562122 200
从上能够看到是先发送的获取baidu的相关信息,然后依次是126,sina,但是收到数据的先后顺序不一定与发送顺序相同,这也就体现出了异步,即不确定什么时候会收到数据,顺序不一定
强化理解 利用猴子黑科技动态地将IO操作库中的函数替换成gevent 自己的函数,之后再次遇到io阻塞操作时自动切换。执行
优点:不存在资源竞争
缺点:手动调用
gevent版-TCP服务器
1 import sys 2 import time 3 import gevent 4 5 from gevent import socket,monkey 6 monkey.patch_all() 7 8 def handle_request(conn): 9 while True: 10 data = conn.recv(1024) 11 if not data: 12 conn.close() 13 break 14 print("recv:", data) 15 conn.send(data) 16 17 18 def server(port): 19 s = socket.socket() 20 s.bind(('', port)) 21 s.listen(5) 22 while True: 23 cli, addr = s.accept() 24 gevent.spawn(handle_request, cli) 25 26 if __name__ == '__main__': 27 server(7788)
进程vs线程vs协程
进程 并行执行 无序
线程 一次一个并发执行 无序
协程 一次一个手动挑用 手动调用

浙公网安备 33010602011771号