• Python使用yield协程实现生产者消费者问题


    目录

    协程

    官网定义

    通俗理解

    协程的优势

    Python协程使用方法

    代码示范

    运行结果

    yield实现生产者消费者

    定义消费者

    定义生产者

    程序运行输出

    分析


    协程

    官网定义

            协程是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。

    通俗理解

            协程又被称为微线程,协程的完成主要靠yield关键字,协程执行过程中,在子程序内部可中断,将当前运行的上下文挂起,然后转而执行别的子程序,在适当的时候再返回来接着执行。

    协程的优势

            python中实现并发的方式有很多种,通过多进程并发可以真正利用多核资源,而多线程并发则实现了进程内资源的共享,然而Python中由于GIL锁机制,多线程实际上是伪多线程;
            对于计算密集型程序,应该使用多进程并发充分利用多核资源,而在IO密集型程序中,多核优势并不明显,甚至由于大多数时间都是在IO堵塞状态,多进程的切换消耗反而让程序效率更加低下;
            而当需要并发处理IO密集型任务时,就需要用到协程(Coroutine)。协程并没有系统级的调度,而是用户级的调度方式,避免了系统调用的开销,虽然协程最终是串行工作,但是却可以实现非常大的并发量。通过多进程+协程的方式,可以有效均衡多核计算和请求等待。

    Python协程使用方法

    1.带有yield关键字的函数自动变成生成器

    2.生成器被调用时不会立即执行

    3.对于生成器,当调用函数next(generator)时,将获得生成器yield后面表达式的值;

    4.当生成器已经执行完毕时,再次调用next函数,生成器会抛出StopIteration异常

    代码示范

    定义一个使用yield的foo()方法

    1. def foo():
    2. while True:
    3. print("======这是yield之前前前前====")
    4. x = yield
    5. print("x当前值:{0}".format(x))
    6. print("======这是yield之后后后后====")

    使用next()和send()

    1. if __name__ == '__main__':
    2. g = foo()
    3. # 第一次调用next的时候,程序从函数最开始处运行,执行到yield处,停在该处
    4. next(g)
    5. print("\n=====================下 一 个====================\n")
    6. next(g)
    7. print("\n=====================下 一 个====================\n")
    8. # send将参数赋给yield的返回值,然后该返回值赋给了变量x
    9. # 继续程序的执行,直到下一次遇到yield停下来
    10. g.send(1)
    11. # next就相当于send(None)
    12. print("\n=====================下 一 个====================\n")
    13. g.send(None)

    运行结果

            我们可以发现,实际上就是在yield关键字的位置停止程序向下执行,同时记住当前执行到的位置,下次继续在这个位置向后执行,直到结束或者又遇到yield。

            我们发现,整个程序过程中没有用到锁,因为协程只有一个线程,也不存在同时写变量冲突,所以在协程中控制共享资源不需要加锁,只需要判断状态即可。

    yield实现生产者消费者

    定义消费者

    1. def cousume():
    2. r = ''
    3. while True:
    4. n = yield r
    5. if not n:
    6. return
    7. print("消费者说:消费者消费了第{0}个".format(n))
    8. time.sleep(1)
    9. r = '用了'

    定义生产者

    1. def produce(c):
    2. next(c)
    3. n = 0
    4. while n < 3:
    5. n += 1
    6. print("生产者说:生产者生产了第{0}个:".format(n))
    7. r = c.send(n)
    8. print("生产者说:消费者反馈-- {0}".format(r))
    9. c.close()

    程序运行输出

    1. if __name__ == '__main__':
    2. c = cousume()
    3. produce(c)
    4. print("结束")

    分析

    1. 首先调用consume函数。consume函数的返回是一个生成器,把这个生成器传入produce函数。
    2. produce函数中调用next(c)启动生成器。
    3. 计算n =n + 1生成数据,一旦生产了数据,调用c.send(n)切换到consume执行。
    4. consume函数中拿到数据后赋值给n,继续执行yield后面的语句
    5. consume函数中打印消费的数据,并设置返回值r,又回到循环的开始,通过yield把结果传回。
    6. produce拿到consume返回的值,继续生产下一个数据。
    7. 数据生产完毕后,循环结束,通过c.close()关闭consume,结束全过程

        注意

            produce和consume函数是在一个线程内执行,通过调用send方法和yield互相切换

    子程序就是协程的一种特例。   —— 唐纳德·克努特

    欢迎点赞、收藏、评论区交流,转载标明出处。

  • 相关阅读:
    神经网络:基本概念、模型与技术
    Linux卷组管理
    100行代码实现HarmonyOS“画图”应用,eTS开发走起!
    无涯教程-Flutter - 数据库
    easypoi自定义模板导出
    【LeetCode】141.环形链表
    观察者模式(大话设计模式)C/C++版本
    电子学会python三级笔记
    QVHZO-A-06/18、QVHZE-A-06/36比例流量控制阀放大器
    HTB靶场 Perfection
  • 原文地址:https://blog.csdn.net/qq_52213943/article/details/127750741