什么是协程?
并发编程一般都是使用多线程或者多进程来实现的,对于CPU计算密集型任务由于GIL的存在通常使用多进程来实现,而对于IO密集型任务可以通过线程调度来让线程在执行IO任务时让出GIL,从而实现表面上的并发。
其实对于IO密集型任务我们还有一种选择就是协程。协程,又称微线程,英文名Coroutine,是运行在单线程中通过类似于CPU中断来达到“并发”目的的。它相比多线程的一大优势就是省去了多线程之间的切换开销,获得了更高的运行效率。另一方面,由于只有一个线程,不存在同时写操作冲突,不需要多线程的锁机制。
多说无益,下面来做个实验具体感受一下协程的性能优势:
这里借用廖雪峰老师网站的关于协程的一个例子, 实验的设计思路是使用协程和多线程模式来实现简单的生产者-消费者模式, 然后对比两种情况下的性能:
代码实现:
测试代码:
import time
from functools import wraps
# A simple time it decorator
def timethis(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
r = func(*args, **kwargs)
end = time.time()
print('%s takes %s' % (func.__name__, end - start))
return r
return wrapper
if __name__ == '__main__':
import t_thread
import t_async
func1 = timethis(t_thread.thread_main)
func2 = timethis(t_async.async_main)
func1(100000)
func2(100000)
在操作数为100,000的情况下的测试结果:
thread_main takes 1.127411127090454
async_main takes 0.019986867904663086
笔者又测了一组操作次数在10,000的情况下的测试结果:
thread_main takes 0.05496549606323242
async_main takes 0.003996610641479492
注:实际测试了多组数据,数量级差异基本稳定,这里只取了其中一组数据。
结论:
可以看出,这种情况下 协程 较之 线程 有明显提升, 而且这种趋势随着操作次数增多基本成线性增长。
附录:
协程的代码实现,为了最后打印信息干净这里注释掉了print, t_async.py
:
def consumer():
r = ''
while True:
n = yield r
if not n:
return
# print('[ASYNC_CONSUMER] Consuming %s...' % n)
r = '200 OK'
def producer(c, count):
c.send(None)
n = 0
while n < count:
n = n + 1
# print('[ASYNC_PRODUCER] Producing %s...' % n)
r = c.send(n)
# print('[ASYNC_PRODUCER] Consumer return: %s' % r)
c.close()
def async_main(count):
c = consumer()
producer(c, count)
笔者的个人观点, 协程的语法相对来说没有线程操作那么明朗, 也可能多线程编程是先入为主的缘故吧。
多线程的代码实现 t_thread.py
:
import queue
import threading
_sentinel = object()
q = queue.Queue()
def consumer(in_q):
while True:
n = in_q.get()
if n is _sentinel:
in_q.put(_sentinel)
break
# print('[THREAD_CONSUMER] Consuming %s...' % n)
def producer(out_q, count):
n = 0
while n < count:
n += 1
# print('[THREAD_PRODUCER] Producing %s...' % n)
out_q.put(n)
out_q.put(_sentinel)
def thread_main(count):
c = threading.Thread(target=consumer, args=(q,))
p = threading.Thread(target=producer, args=(q, count))
p.start()
c.start()
c.join()
网友评论