协程笔记说明

徘徊边缘 提交于 2020-02-22 16:29:47

协程的定义

协程是python个中另外一种实现多任务的方式,只不过比线程更小占用更小执行单元(理解为需要的资源)。 为啥说它是一个执行单元,因为它自带CPU上下文。这样只要在合适的时机, 我们可以把一个协程 切换到另一个协程。 只要这个过程中保存或恢复 CPU上下文那么程序还是可以运行的。

通俗的理解:在一个线程中的某个函数,可以在任何地方保存当前函数的一些临时变量等信息,然后切换到另外一个函数中执行,注意不是通过调用函数的方式做到的,并且切换的次数以及什么时候再切换到原来的函数都由开发者自己确定。

通过使用yield完成多任务

import time
def task_1():
    while True:
        print("---1----")
        time.sleep(0.1)
        yield
        
def task_2():
    while True:
        print("---2----")
        time.sleep(0.1)
        yield
        
def main():
    t1 = task_1()
    t2 = task_2()
    # 先让t1运行一会,当t1中遇到yield的时候,再返回到t2,然后
    # 执行t2,当它遇到yield的时候,再次切换到t1中
    # 这样t1/t2/t1/t2的交替运行,最终实现了多任务....协程
    while True:
        next(t1)
        next(t2)
        
if __name__ == "__main__":
    main()

协程调用一个任务跟调用一个函数一样,消耗的资源非常少,核心就是生成器的使用。

greenlet进行封装来替换生成器

sudo pip3 install greenlet安装greenlet

from greenlet import greenlet
import time

def test1():
    while True:
        print("---A--")
        gr2.switch()
        time.sleep(0.5)

def test2():
    while True:
        print("---B--")
        gr1.switch()
        time.sleep(0.5)

gr1 = greenlet(test1)
gr2 = greenlet(test2)

#切换到gr1中运行
gr1.switch()

gevent实现多任务

sudo pip3 install gevent安装 gevent实现协程

import gevent
import time

def f1(n):
    for i in range(n):
        print(gevent.getcurrent(), i)
        # time.sleep(0.5)    #  用time的不能切换
        gevent.sleep(0.5)   #   用gevent自带的time进行延时,
                      # 而在延时的时候就会进行一个切换到其他函数
def f2(n):
    for i in range(n):
        print(gevent.getcurrent(), i)
        # time.sleep(0.5)
        gevent.sleep(0.5)

def f3(n):
    for i in range(n):
        print(gevent.getcurrent(), i)
        # time.sleep(0.5)
        gevent.sleep(0.5)

print("----1---")
g1 = gevent.spawn(f1, 5)
print("----2---")
g2 = gevent.spawn(f2, 5)
print("----3---")
g3 = gevent.spawn(f3, 5)
print("----4---")
g1.join()
g2.join()
g3.join()

多协程最大意义在于利用原来等待耗时的时间,来做别的事情。协程依赖于线程,线程依赖于进程。

不依赖gevent的自带延时和阻塞切换

为了适配常用的延时和阻塞函数和操作,可引入monkey进行自动切换,进行打补丁。

from gevent import monkey
import gevent
import random
import time

# 有耗时操作时需要
monkey.patch_all()  # 将程序中用到的耗时操作的代码,换为gevent中自己实现的模块

def coroutine_work(coroutine_name):
    for i in range(10):
        print(coroutine_name, i)
        time.sleep(random.random())
        
#  用gevent.joinall等待全部来代替一个个做等待,实现简洁化代码
gevent.joinall([
        gevent.spawn(coroutine_work, "work1"),
        gevent.spawn(coroutine_work, "work2")
])

协程和线程差异

在实现多任务时, 线程切换从系统层面远不止保存和恢复 CPU上下文这么简单。 操作系统为了程序运行的高效性每个线程都有自己缓存Cache等等数据,操作系统还会帮你做这些数据的恢复操作。 所以线程的切换非常耗性能。但是协程的切换只是单纯的操作CPU的上下文,所以一秒钟切换个上百万次系统都抗的住。

进程、线程和协程对比

  • 进程是资源分配的单位
  • 线程是操作系统调度的单位
  • 进程切换需要的资源很最大,效率很低
  • 线程切换需要的资源一般,效率一般(当然了在不考虑GIL的情况下)
  • 协程切换任务资源很小,效率高
  • 多进程、多线程根据cpu核数不一样可能是并行的,但是协程是在一个线程中 所以是并发
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!