• 并发编程--协程


      协程,又称为微线程,可以理解成可切换的函数,或生成器,协程中始终在单线程中执行,因此没有资源冲突问题,不需要锁机制。以下以菲波那切数列为例,加上自己的一些理解,稍微聊一下这个东西。

    斐波那契数列的普通实现

    一般的函数只能有一个返回值,return,且return后程序不再执行。如下:

    # 斐波那契数列的普通实现
    def fib(n):
        res = [0] * n
        index = 0
        a = 0
        b = 1
        while index < n:
            res[index] = b
            a, b = b, a+b  # 交换位置
            index += 1
        return res
    # print(fib(5))
    """
    fib是一个普通函数,只能在return返回
    """

    斐波那契数列yield实现

    通过yield实现一个生成器对象,得到该数列。yield生成器每next或__next__()就调用一次,每次调用程序执行到yield时返回,并悬挂;等待下一次next调用。

    # 斐波那契的yield实现
    def fib_1(n):
        index = 0
        a = 0
        b = 1
        while index < n:
            yield b  # 返回b,每次执行到此处,返回一次,悬挂程序,每调用一次__next__() 执行一次
            a, b = b, a+b
            index += 1
    
    # f = fib_1(5)
    # print(f.__next__())
    # print(f.__next__())
    # print(f.__next__())
    # print(f.__next__())
    # print(f.__next__())
    """
    此时的fib_1是一个生成器对象,程序每次执行到yield b 处就返回一个b,并悬挂程序
    等待下一次next的调用,
    下一次调用__next__()时, 程序从悬挂处继续执行(执行下面的代码)
    继续执行到yield b处, 返回此时的b并悬挂程序,等待下一次的调用,直到满足循环条件,停止迭代
    满足条件后,生成器没有数据可取,若继续调用则程序报错
    """

    斐波那契数列传参

    生成器在执行过程中,可以返回值,同样也可以在执行时给它传参。传参关键字send因此,可以写两个函数,一个yield返回值,一个send给它发送值。

    def fib_2(n):
        index = 0
        a = 0
        b = 1
        while index < n:
            c = yield b  # 调用next时悬挂,生成器返回b的同时接收传参,并用c记录先来
            print("-->send:{}".format(c))
            time.sleep(c)
            a, b = b, a+b
            index += 1
    
    # f = fib_2(10)
    # print(next(f))

    c = yield b 中,b和c不存在赋值关系,yield b 时程序已给出返回值,=也不能理解为赋值此处的等号“=” 相当于一个管道,管道的一端用于返回b(即处理yield b),管道的另一端用于接收send的传参,并用c记录传过来的值,提供给程序后面利用
    仅个人理解,不一定对。。。。。。

    继续在上面代码中添加:

    # while True:
    #     f.send(random.randint(1, 3))

    """
    程序在函数内部和外部两边切换执行,
    程序首先执行到yield b是,fib_2已返回,切换到函数外部,
    不再执行fib_2内部代码,而是执行函数外部的while True;
    直到执行到send时,切换到函数内部,继续执行函数内部的代码,
    直到执行到函数内部的yield时,又切换到函数外部
    循环往复。。。。。
    """

    使用yield实现一个生产者、消费者模型

    # 使用yield实现一个生产者、消费者模型
    import time
    def Consumer():
        r = ''
        while True:
            n = yield r
            if not n:
                return
            print('消费<{}>中...'.format(n))
            time.sleep(1)
            r = '200'
    
    def Producer(c):
        c.send(None)  # 启动生成器,将代码运行至yield这一行,并返回(相当于next调用)
        n = 1
        while n < 6:
            print("生产<{}>中...".format(n))
            time.sleep(1)
            r = c.send(n)  # 给生成器发送值,执行权交给consumer
            print("已消费!status_code:{}
    ".format(r))
            n += 1
        c.close()
    
    c = Consumer()  # 得到一个生成器,由于生成器没有调用,因此没有执行
    Producer(c)  # 函数调用,执行到send时,启动生成器consumer(相当于next调用)

    执行结果:

    具体代码如下:

    """
    协程,又称为微线程
    
    补充知识点
    iterable(可迭代对象), 可用于for循环的对象
    itertor(迭代器), 可用于next取值的对象
    iterion(迭代)
    
    """
    # 斐波那契数列的普通实现
    def fib(n):
        res = [0] * n
        index = 0
        a = 0
        b = 1
        while index < n:
            res[index] = b
            a, b = b, a+b  # 交换位置
            index += 1
        return res
    # print(fib(5))
    """
    fib是一个普通函数,只能在return返回
    """
    
    # 斐波那契的yield实现
    def fib_1(n):
        index = 0
        a = 0
        b = 1
        while index < n:
            yield b  # 返回b,每次执行到此处,返回一次,悬挂程序,每调用一次__next__() 执行一次
            a, b = b, a+b
            index += 1
    
    # f = fib_1(5)
    # print(f.__next__())
    # print(f.__next__())
    # print(f.__next__())
    # print(f.__next__())
    # print(f.__next__())
    """
    此时的fib_1是一个生成器对象,程序每次执行到yield b 处就返回一个b,并悬挂程序
    等待下一次next的调用,
    下一次调用__next__()时, 程序从悬挂处继续执行(执行下面的代码)
    继续执行到yield b处, 返回此时的b并悬挂程序,等待下一次的调用,直到满足循环条件,停止迭代
    满足条件后,生成器没有数据可取,若继续调用则程序报错
    """
    
    # 斐波那契数列传参
    """
    生成器在执行过程中,可以返回值,同样也可以在执行时给它传参。
    传参关键字send
    因此,可以写两个函数,一个yield返回值,一个send给它发送值
    """
    import time
    
    def fib_2(n):
        index = 0
        a = 0
        b = 1
        while index < n:
            c = yield b  # 调用next时悬挂,生成器返回b的同时接收传参,并用c记录先来
            print("-->send:{}".format(c))
            time.sleep(c)
            a, b = b, a+b
            index += 1
    
    # f = fib_2(10)
    # print(next(f))
    """
    c = yield b 中,b和c不存在赋值关系,
    yield b 时程序已给出返回值,=也不能理解为赋值
    此处的等号“=” 相当于一个管道,
    管道的一端用于返回b(即处理yield b),
    管道的另一端用于接收send的传参,并用c记录传过来的值,提供给程序后面利用
    
    仅个人理解,不一定对。。。。。。
    """
    
    
    # while True:
    #     f.send(random.randint(1, 3))
    """
    程序在函数内部和外部两边切换执行,
    程序首先执行到yield b是,fib_2已返回,切换到函数外部,
    不再执行fib_2内部代码,而是执行函数外部的while True;
    直到执行到send时,切换到函数内部,继续执行函数内部的代码,
    直到执行到函数内部的yield时,又切换到函数外部
    循环往复。。。。。
    """
    
    # 协程理解:可以切换的函数,或生成器
    # 协程始终在单线程中执行,因此没有资源冲突问题,不需要锁机制
    
    # 使用yield实现一个生产者、消费者模型
    import time
    def Consumer():
        r = ''
        while True:
            n = yield r
            if not n:
                return
            print('消费<{}>中...'.format(n))
            time.sleep(1)
            r = '200'
    
    def Producer(c):
        c.send(None)  # 启动生成器,将代码运行至yield这一行,并返回(相当于next调用)
        n = 1
        while n < 6:
            print("生产<{}>中...".format(n))
            time.sleep(1)
            r = c.send(n)  # 给生成器发送值,执行权交给consumer
            print("已消费!status_code:{}
    ".format(r))
            n += 1
        c.close()
    
    c = Consumer()  # 得到一个生成器,由于生成器没有调用,因此没有执行
    Producer(c)  # 函数调用,执行到send时,启动生成器consumer(相当于next调用)
  • 相关阅读:
    poj 3122 Pie (二分)
    poj 1905 Expanding Rods(二分)
    poj 3258 River Hopscotch (二分)
    poj 3273 Monthly Expense(二分穷举)
    最小最大堆
    最小—最大堆
    zend studio 9 字体,颜色,快捷键等相关设置
    javascript onbeforeunload
    php DOMDocument 不能解析引用外部DTD的XML
    phpdoc 注释关键字含义
  • 原文地址:https://www.cnblogs.com/pythoner6833/p/9007496.html
Copyright © 2020-2023  润新知