第10章 python多任务-协程

同步、异步

  • 同步:是指代码调用IO操作时,必须等待IO操作完成才返回的调用方式
  • 异步:是指代码调用IO操作时,不必等IO操作完成就返回的调用方式

阻塞、非阻塞

  • 阻塞:从调用者的角度出发,如果在调用的时候,被卡住,不能再继续向下运行,需要等待,就说是阻塞
  • 非阻塞:从调用者的角度出发, 如果在调用的时候,没有被卡住,能够继续向下运行,无需等待,就说是非阻塞

1、生成器-send方法

send方法有一个参数,该参数指定的是上一次被挂起的yield语句的返回值

'''
生成器的send方法
'''
def create_num(num):
    #生成器的send方法
    a, b = 0, 1
    current_num = 0

    while current_num < num:
        result = yield a    # result = yield a = 'juran'
        print('result-->',result)
        a, b = b, a+b
        current_num += 1

    return 'campo'

if __name__ == '__main__':

    g = create_num(5)

    #for循环可以取出生成器的值
    # for i in g:
    #     print(i)

    # #next(g)也可以取出生成器的值
    # try:
    #     while True:
    #         print(next(g))
    # except Exception as e:
    #     print('==:', e)
    #
    #
    # #send方法启动生成器,第一次调用send必须带参数“None”
    print(g.send(None))
    print(g.send('juran'))
    print(g.send('amy'))
    # g.close() 方法关闭生成器,后面不能再调用生成器
    g.close()
    print(g.send('jerry'))

上面程序的返回值是:其中包含报错信息,因为调用了 g.close() 关闭了生成器,所以,再次 g.send(‘jerry’) 会报错。
在这里插入图片描述

2、使用yield完成多任务

'''
使用yeild完成多任务
'''
import time


def task1():
    while True:
        print('--1--')
        time.sleep(0.1)
        yield


def task2():
    while True:
        print('--2--')
        time.sleep(0.1)
        yield


def main():
    t1 = task1()
    t2 = task2()
    while True:
        next(t1)  #输出 --1-- 到yield 等待,执行 next(t2)
        next(t2)  #输出 --2-- 到yield 等待,返回到 while True: 继续循环执行 next(t1)


if __name__ == '__main__':
    main()

上面代码返回值,交替返回1 和 2
在这里插入图片描述

3、yield from介绍

3.1 python3.3新加了yield from语法

lis = [1, 3, 5]
dic = {
    'name':'juran',
    'age':18
}

def my_chain(*args, **kwargs):
    for my_iterable in args:
    	# for value in my_iterable:
        #     yield value
        #yeild from 代替了上面两行 for 循环,可以得到同样的结果
        yield from my_iterable

for value in my_chain(lis, dic, range(5, 10)):
    print(value)

上面代码返回结果如下:
在这里插入图片描述

3.2 yield 和 yield from 对比

3.2.1 yield 和 yield from 简单对比


def generator_1(lis):
    yield lis


def generator_2(lis):
    yield from lis


for i in generator_1(lis):
    print('yield 返回值:',end='')
    print(i)

print('yield from 返回值:')
for i in generator_2(lis):
    print(i)

上面代码返回结果:yield 返回的是lis列表,yield from 返回的是单独的1,2,3的值,就像是把 lis 拆包了一样。
在这里插入图片描述

3.2.1 yield 和 yield from 案例分析

【子生成器】:yield from后的generator_1()生成器函数是子生成器
【委托生成器】:generator_2()是程序中的委托生成器,它负责委托子生成器完成具体任务。
【调用方】:main()是程序中的调用方,负责调用委托生成器。

'''子生成器'''
def generator_1():
    total = 0
    while True:
        x = yield
        print('加', x)
        if not x:
            break
        total += x
    return total


'''委托生成器'''
def generator_2():
    while True:
        # 建立调用方和字生成器的通信
        total = yield from generator_1()
        print('加和总数是:', total)


def main():
    '''g1调用方'''
    # g1 = generator_1()
    # g1.send(None)
    # g1.send(2)
    # g1.send(3)
    # g1.send(None)

    '''g2调用方'''
    g2 = generator_2()
    g2.send(None)
    g2.send(2)
    g2.send(3)
    g2.send(None)


if __name__ == '__main__':
    main()

上面代码直接调用 g1 会报错:
在这里插入图片描述

上面代码委托生成器 g2 调用子生成器 g1 返回结果:g2 默认处理了g1返回的错误
在这里插入图片描述

4、协程

协程,又称微线程
协程是python中另外一种实现多任务的方式,只不过比线程更小占用更少执行单元(理解为需要的资源)

Python中的协程大概经历了如下三个阶段:

  • 最初的生成器变形yield/send
  • yield from
  • 在最近的Python3.5版本中引入async/await关键字

4.1 使用greenlet完成多任务

安装模块:pip3 install greenlet

from greenlet import greenlet
import time


def demo1():
    while True:
        print('demo1')
        gr2.switch()
        time.sleep(0.5)


def demo2():
    while True:
        print('demo2')
        gr1.switch()
        time.sleep(0.5)


if __name__ == '__main__':
    gr1 = greenlet(demo1)
    gr2 = greenlet(demo2)

    gr1.switch()


上面程序返回值:demo1 和 demo2 交替打印
在这里插入图片描述

4.2 使用gevent完成多任务

安装模块:pip3 install gevent

简单总结
进程是资源分配的单位
线程是操作系统调度的单位
进程切换需要的资源很最大,效率很低
线程切换需要的资源一般,效率一般(当然了在不考虑GIL的情况下)
协程切换任务资源很小,效率高
多进程、多线程根据cpu核数不一样可能是并行的,但是协程是在一个线程中 所以是并发

import gevent
import time
from gevent import monkey

# 将程序中用到的耗时操作  换为gevent中实现的模块
monkey.patch_all()


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


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


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


def main():
    # global g1, g2, g3
    g1 = gevent.spawn(fn1, 3)
    g2 = gevent.spawn(fn2, 5)
    g3 = gevent.spawn(fn3, 7)

    g1.join()
    g2.join()
    g3.join()


if __name__ == '__main__':
    main()

上面程序返回值是:
在这里插入图片描述

发布了31 篇原创文章 · 获赞 0 · 访问量 341

猜你喜欢

转载自blog.csdn.net/weixin_38027481/article/details/104049855