网络编程四:协程

子程序/子函数:在所有语言中,都是层级调用。

比如函数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)    收藏  举报

导航