协程与IO
协程的出现出现为克服同步模型和异步模型的缺点,并结合他们的优点提供了可能: 现在假设我们有3个协程A,B,C分别要进行数次IO操作。
这3个协程运行在同一个调度器或者说线程的上下文中,并依次使用CPU。调度器在其内部维护了一个多路复用器(epoll/select/poll)。
- 协程A首先运行,当它执行到一个IO操作,但该IO操作并没有立即就绪时,A将该IO事件注册到调度器中,并主动放弃CPU。
- 这时调度器将B切换到CPU上开始执行,同样,当它碰到一个IO操作的时候将IO事件注册到调度器中,并主动放弃CPU。
- 调度器将C切换到cpu上开始执行。
- 当所有协程都被“阻塞”后,调度器检查注册的IO事件是否发生或就绪。**假设此时协程B注册的IO时间已经就绪,调度器将恢复B的执行,B将从上次放弃CPU的地方接着向下运行。**A和C同理。
这样,对于每一个协程来说,它是同步的模型;但是对于整个应用程序来说,它是异步的模型。
进程 协程 线程
多核CPU,CPU密集型应用
此时多线程的效率是最高的,多线程可以使到全部CPU核心满载,又避免了协程间切换造成性能损失。当CPU密集型任务时,CPU一直在利用着,切换反而会造成性能损失,即便协程上下文切换消耗最小,但也还是有消耗的。多核CPU,IO密集型应用
此时采用多线程多协程效率最高,多线程可以使到全部CPU核心满载,而一个线程多协程,则更好的提高了CPU的利用率。单核CPU,CPU密集型应用
单进程效率是最高,此时单个进程已经使到CPU满载了。单核CPU,IO密集型应用
多协程,效率最高。例如,看了上面应该也是知道的了
例子
传统的生产者-消费者模型是一个线程写消息,一个线程取消息,通过锁机制控制队列和等待,但一不小心就可能死锁。
如果改用协程,生产者生产消息后,直接通过yield跳转到消费者开始执行,待消费者执行完毕后,切换回生产者继续生产,效率极高:
1.用多线程的思路
import threading, time
import Queue #导入消息队列模块
import random #导入随机数模块,是为了模拟生产者与消费者速度不一致的情形
q = Queue.Queue() #实例化一个对象
def Producer(name): #生产者函数
for i in range(20):
q.put(i) #将结果放入消息队列中
print '\033[32;1mProducer %s has made %s baozi....\033[0m' % (name, i)
time.sleep(random.randrange(3)) #生产者的生产速度,3s内
def Consumer(name): #消费者函数
count = 0
while count < 20:
data = q.get() #取用消息队列中存放的结果
print '\033[31;1mConsumer %s has eatem %s baozi...chihuo...\033[0m' % (name, data)
count += 1
time.sleep(random.randrange(4)) #消费者的消费速度,4s内
p = threading.Thread(target = Producer, args = ('Alex',))
c = threading.Thread(target = Consumer, args = ('Wangfanhao',))
p.start()
c.start()
2.用协程的思路
def consumer():
r = ''
while True:
n = yield r
if not n:
return
print('[CONSUMER] Consuming %s...' % n)
r = '200 OK'
def produce(c):
c.send(None)
n = 0
while n < 5:
n = n + 1
print('[PRODUCER] Producing %s...' % n)
r = c.send(n)
print('[PRODUCER] Consumer return: %s' % r)
c.close()
c = consumer()
produce(c)