并发编程--协程

Posted 温良Miner

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了并发编程--协程相关的知识,希望对你有一定的参考价值。

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

斐波那契数列的普通实现

一般的函数只能有一个返回值,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:{}\\n".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:{}\\n".format(r))
        n += 1
    c.close()

c = Consumer()  # 得到一个生成器,由于生成器没有调用,因此没有执行
Producer(c)  # 函数调用,执行到send时,启动生成器consumer(相当于next调用)

 

以上是关于并发编程--协程的主要内容,如果未能解决你的问题,请参考以下文章

Python并发编程理解yield from协程

python 并发编程 协程 目录

并发编程-协程

Python并发编程——多线程与协程

python并发编程--协程

python语法基础-并发编程-协程-长期维护