网络编程四:协程
子程序/子函数:在所有语言中,都是层级调用。
比如函数A调用B,在B执行的过程中又调用C;C执行完毕返回,B执行完毕返回,最后是A执行完毕。
这个过程是通过栈实现的,一线程就是执行一个子程序,子程序调用总是一个入口一次返回,调用的顺序是明确的
def a(): print ("a --- start") b() print ("a --- end") def b(): print ("b --- start") c() print ("b --- end") def c(): print ("c --- start") print ("c --- end") a()
a --- start b --- start c --- start c --- end b --- end a --- end
协程:看上去也是子程序,像上面的调用过程,但在执行过程中,在子程序的内部可中断;在中断后,转而执行别的子程序,不是函数调用,不是上面的调用过程;类似于CPU调用。
示例:
def a(): print(1) print(2) print(3) def b(): print("x") print("y") print("z")
对于以上代码,如果在同一个线程中依次调用a, b函数,得到的结果必然是顺序的:
"1 2 3 x y z"
对于以上代码,如果a, b在不同线程中执行,得到的顺序是不确定的:
"1 2 3 x y z 3"
对于协程,协程是在一个线程中执行,但得到的效果,像是多个线程执行得到的效果。即得到的结果,不是顺序的,而是不确定的,如上。
与线程相比,协程的执行效率极高;因为只有一个线程,也不存在同时写变量的冲突,共享资源不需要加锁,只需要判断状态即可。
python对协程的支持,是通过generator实现的。
协程的示例:
def run(): print(1) yield 10 print(2) yield 20 print(3) yield 30 # 这是最简单的协程风格,控制函数的阶段执行,节约线程或进程的切换 #返回值 是一个生成器 m = run()
以上执行结果,得到的是一个生成器,不会打印任何东西,包括print(1)
用next调用生成器:
print(next(m)) # 1 # 20
print(next(m)) print(next(m)) 1 10 # 这是yield,即生成器的返回值 2 20 # 这是yield,即生成器的返回值
可见,协程对函数做到了分段执行,就像多个线程的执行一样。yield也会像return一样,会有返回值;返回值为yield后面的值。
对yield进行赋值:
def run(): data = "data" r = yield data print(1, r, data) r = yield data print(2, r, data) r = yield data print(3, r, data) m = run() print(next(m)) print(next(m)) print(next(m)) print(next(m))
执行结果,如下:
data #这是yield的返回值 1 None data # 第二次yield时,yield前面的代码 data #这是yield的返回值 2 None data # 第三次yield时,yield前面的代码 data #这是yield的返回值 3 None data # 第四次yield时,yield前面的代码 # StopIteration异常 # 这是第四次yield时,抛出的异常
从以上结果,可以看出r始终为空值,可见r并不是yield的返回值。
那么r是什么?r是生成器调用send方法时的send值。send方法,不仅向yield传送了值(不是返回值),而且相当于执行了一次next方法。
# 启动生成器m print(m.send(None)) print(m.send("x")) print(m.send("y")) print(m.send("z")) # 执行第四次yield前面的代码,因为没有第四个yield,抛出异常 print(m.send("abc")) #不会执行
data #第一次yield返回值 1 x data # 第二次yield时,yield前面的部分代码,包括 r= 这部分,但不包括yield data这部分 data #第二次yield返回值 2 y data # 第三次yield时,yield前面的部分代码 data # 第三次yield的返回值 3 z data # 第四次yield时,yield前面的部分代码。同时将抛出异常,因为没有第四个yield
首次调用时,不可以用send()方法去赋(发送)一个非None值,因为第一次执行时,只会执行到yield data这步,r = 赋值这步不会执行到。如果第一次send了一个非None值,将会出现,发送的值没有yield表达式去接收,将会引发一个TypeError异常
send()方法,有返回值,返回值为yield的内容:
# 启动生成器m a=m.send(None) print(a) b=m.send("x") print(b) c=m.send("y") print(c) d=m.send("z") print(d)
结果,同上面一样。
使用协程,实现一个生产者消费者模型:
def producer(c): print(c.send(None)) for i in range(3): print("生产者生产了数据", i) r = c.send(str(i)) print("消费者消费了数据", r) c.close() def comsumer(): data = "data" print(">>>", data) while True: n = yield data if not n: break print("消费者消费了", n) data = "200" c = comsumer()
print(next(c)) >>> data data
print(producer(c))
posted on 2018-11-03 09:29 myworldworld 阅读(103) 评论(0) 收藏 举报