并发编程--协程
协程,又称为微线程,可以理解成可切换的函数,或生成器,协程中始终在单线程中执行,因此没有资源冲突问题,不需要锁机制。以下以菲波那切数列为例,加上自己的一些理解,稍微聊一下这个东西。
斐波那契数列的普通实现
一般的函数只能有一个返回值,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调用)