目录
1、协程的优势:
- 协程的执行效率高。因为子程序切换不是线程切换,而是由程序自身控制。因此,没有线程切换的开销,和多线程相比,线程数量越多,相同数量的协程体现出的优势越明显
- 不需要多线程的锁机制。由于只有一个线程,也不存在同时写变量的冲突,在协程中控制共享资源不需要加锁,只需要判断数据的状态,所以执行效率远高于线程 ,对于多核CPU可以使用多进程+协程来尽可能高效率地利用CPU。
- 通俗易懂的回答:让原来要使用异步+回调方式写的非人类代码,可以用看似同步的方式写出来...
2、生产者消费者协程模型
(1)yield方式
import time
def consumer():
r = ''
while True:
n = yield r
if not n:
return
print('[消费者] <--- Consuming %s...' % n)
time.sleep(1)
r = 'ok'
def producer(c):
next(c)
n = 0
while n < 5:
n = n + 1
print('[生产者] ---> Producing %s...' % n)
# 和next有同样的同能,可以触发函数到下一个yield,
# 区别于next的是可以像yield的左边的变量传值,例如上面yield左边的n
c_ret = c.send(n)
print('[生产者] Consumer return %s' % c_ret)
c.close()
if __name__ == '__main__':
c = consumer()
producer(c)
结果如下:
[生产者] ---> Producing 1...
[消费者] <--- Consuming 1...
[生产者] Consumer return ok
[生产者] ---> Producing 2...
[消费者] <--- Consuming 2...
[生产者] Consumer return ok
[生产者] ---> Producing 3...
[消费者] <--- Consuming 3...
[生产者] Consumer return ok
[生产者] ---> Producing 4...
[消费者] <--- Consuming 4...
[生产者] Consumer return ok
[生产者] ---> Producing 5...
[消费者] <--- Consuming 5...
[生产者] Consumer return ok
使用yield手动实现协程是比较麻烦的,Python提供了greenlet和gevent模块用来实现协程。
(2)greenlet方式
from greenlet import greenlet
import time
def A():
while 1:
print('-------A-------')
time.sleep(0.5)
g2.switch()
def B():
while 1:
print('-------B-------')
time.sleep(0.5)
g1.switch()
g1 = greenlet(A) #创建协程g1
g2 = greenlet(B)
g1.switch() #跳转至协程g1
结果如下:
-------A-------
-------B-------
-------A-------
-------B-------
-------A-------
(3)gevent方式
gevent每次遇到io操作,需要耗时等待时,会自动跳到下一个协程继续执行。
import gevent
def A():
while 1:
print('-------A-------')
gevent.sleep(1) #用来模拟一个耗时操作,注意不是time模块中的sleep
def B():
while 1:
print('-------B-------')
gevent.sleep(0.5) #每当碰到耗时操作,会自动跳转至其他协程
g1 = gevent.spawn(A) # 创建一个协程
g2 = gevent.spawn(B)
g1.join() #等待协程执行结束
g2.join()
结果如下:
-------A-------
-------B-------
-------B-------
-------A-------
-------B-------
-------B-------
-------A-------
-------B-------
-------B-------
3、协程gevent实例
利用gevent完成协程方式的回显服务器
import gevent
from gevent import monkey,socket
monkey.patch_all() #有IO才做时需要这一句
s = socket.socket(2,1) #用的都是gevent模块中的socket,但用法一样
s.setsockopt(1,2,1)
s.bind(('',8080))
s.listen(1024)
def func_accept():
while 1:
cs,userinfo = s.accept()
print('来了一个客户'+str(userinfo))
g = gevent.spawn(func_recv,cs) #每当有用户连接,增加一条协程
def func_recv(cs):
while 1:
recv_data = cs.recv(1024)
print(recv_data) #程谁堵塞了,便会跳转至其他协程
if len(recv_data) > 0:
cs.send(recv_data)
else:
cs.close()
break
g1 = gevent.spawn(func_accept)
g1.join()