美文网首页
Python中协程 vs 线程

Python中协程 vs 线程

作者: 小餐包 | 来源:发表于2020-04-25 20:11 被阅读0次

    什么是协程?

    并发编程一般都是使用多线程或者多进程来实现的,对于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()
    

    相关文章

      网友评论

          本文标题:Python中协程 vs 线程

          本文链接:https://www.haomeiwen.com/subject/onflwhtx.html