生成器复习

在python中,使用了yield的函数被称为生成器(generator).
跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单理解生成器就是一个迭代器.
在调用生成器运行的过程中,每次遇到yield时函数会餐厅并保存当前所有的运行信息,返回yield的值,并在下一次执行next()方法时从当前位置继续运行.
调用一个生成器函数,返回的是一个迭代器对象.
————摘自 noob python3教学。

使用生成器的yield机制来实现多任务

生成器制作方式,在函数中使用yield关键字,生成器。

  1. def work1():
  2. for i in range(10):
  3. print(F'----work1---{i}')
  4. yield
  5. def work2():
  6. for i in range(10):
  7. print(F'----work2---{i}')
  8. yield
  9. # 通过生成器实现多任务
  10. g1 = work1()
  11. g2 = work2()
  12. while True:
  13. next(g1)
  14. next(g2)

这中方式可以称为简单的协程,又叫做微线程。
协程本质上是单任务,只是利用了yield机制在多个任务切换交替执行。
协程依赖于线程。
协程对于线程来说占用的资源更少,几乎没有。

协程概念理解

又称微线程,英文名Coroutine

1,什么是协程

协程是python中一种实现多任务的方式,只不过比线程更小占用,更小执行单元(理解为需要的资源),自带cpu上下文。
通俗的理解,在一个线程中的某个函数,可以在任何地方保存当前函数的一些临时变量等信息。
然后切换到另一个函数中执行,注意不是通过调用函数的方式做到的切换,并且切换的次数以及什么时候切换回来都是由开发者决定。

2,协程与线程差异

在实现多任务时,线程切换从系统层面远不止保存和恢复cpu上下文这么简单,操作系统为了让程序运行的高效每个线程都有自己缓存cache等数据,操作系统还会帮忙做这些数据的恢复工作,所以线程的切换比较耗性能,但是协程的切换只是鹌鹑的操作zpu的上下文,所以一秒钟切换上百万次系统都扛得住

3,greenlet

为了更好的使用协程来完成多任务,python中greenlet对其进行了封装,从而使得切换吧任务变得更加简单,
pip install greenlet

  1. def work1():
  2. for i in range(10):
  3. print(F'----work1---{i}')
  4. g2.switch()
  5. def work2():
  6. for i in range(10):
  7. print(F'----work2---{i}')
  8. g1.switch()
  9. g1 = greenlet(work1)
  10. g2 = greenlet(work2)
  11. g1.switch()

但是greenlet不够优雅,必须要在协程内部写switch方法才能完成切换,可以使用gevent来代替

4,gevent

  1. import gevent
  2. from gevent import monkey
  3. monkey.patch_all() # gevent 的不定
  4. def work1():
  5. for i in range(10):
  6. print(F'----work1---{i}')
  7. gevent.sleep(0.1)
  8. def work2():
  9. for i in range(10):
  10. print(F'----work2---{i}')
  11. gevent.sleep(0.1)
  12. # 创建两个携程
  13. # 线程默认不会等待携程执行
  14. # 携程存在于线程之中
  15. # spawn 开启协程(第一个参数为携程要执行的任务
  16. # join 让线程等待携程执行
  17. # 携程是在什么时候切换的呢?
  18. # 携程之间切换的条件
  19. # gevent.sleep() 耗时等待的情况下才会切换,,不写这个方法就不会切换
  20. # monkey.patch_all() # gevent 补丁 使用一下这个代码就不需要在写gevent也可以交替执行
  21. # 首先考虑携程 --- 线程 --- 进程
  22. g1 = gevent.spawn(work1)
  23. g2 = gevent.spawn(work2)
  24. g1.join()
  25. g2.join()
  26. # 与主线程相比是异步