异步IO:Python中的协程

    我们在实际的问题中会遇到一些堵塞线程的场景,比如UI程序进行耗时的计算阻塞主线程;进行IO操作,比如读写文档或者网络传输时会阻塞线程等等,之前我采用的是使用多进程+多线程的处理方式,但这种方式相比于协程来说都比较慢,特别是在Windows操作系统下启动一个进程的开销是巨大的,因此掌握Python中的协程对于处理一些高并发需求来说是非常重要的,看完了廖雪峰的官方网站–异步IO后我总结了一下我对于协程的理解以及yield, yield from asyc ,await等语法的理解

    Python的协程基础在于生成器,生成器可以在我们需要的时候执行运算,得出执行结果,构造一个生成器有两种方法。

    一是使用():(x * x for x in range(10)) 列表使用中括号[],生成器使用小括号

    二是在函数中使用yield,此时该函数就不是一个函数,而是一个生成器对象

    我们主要使用第二种方法来定义一个生成器,接下来我介绍对于yield, yield from 以及python3.5的新语法async/await

yield

我以一个简单的例子来介绍yield语法:

 def test(times):
     return_info = ''
     n = 0
     while n < times:
         n += 1
         recv = yield n
         print("生成器收到数据%s" % recv)
     print("生成器结束")
generator = test(5)

    上述代码最主要的部分在于 recv = yield n,在等号左边和后边将代码块分成了两部分。

    当我们启动生成器之后,代码会运行到yield n 后中断,并将yield 之后的表达式的值返回给generator.send(“要发送的数据”),也就是return_info = generator.send(“要发送的数据”) 此时return_info == n

    当我们再次通过send(“要发送的数据”)启动生成器时,会从上次中断的地方开始重新启动,也就是从等号的左边开始执行,将send(“要发送的数据”)中的数据赋值给recv,然后继续执行代码直到下一次的yield n后中断。

    我们需要注意的一点是,当我们生成一个生成器generator之后,我们使用generator.send(None)来启动生成器,此时generator.send(None)相当于next(generator),第一次运行generator时必须send(None),因为根据yield语法,第一次执行生成器会在yield n 处中断,此时recv没有接受值,所以只能为None,这一部分在后面了解了yield的运行机制后就会明白,下面简单画一个图

这里写图片描述

2、yield from

    yield from可以很方便地让我们调用另一个generator,yield from 建立了用户端到内层generator之间的”通信”,也可以方便我们在内层generator中处理异常。我们可以看以下代码:

def inner():
    coef = 1
    total = 0
    while True:
        input_val = yield total
        if input_val is 2:
            print('break')
            break
        total = total + coef * input_val
    print("inner end")

def outer2():
    print("Before inner(), I do this.")
    a = yield from inner()
    print(a)
    print("After inner(), I do that.")

c = outer2()
a = c.send(None)
print(a)
print("first send end")
b = c.send(2)
print(b)

输出结果为

Before inner(), I do this.
0
first send end
break
inner end
None
After inner(), I do that.

    当我们启动生成器时,代码会执行到yield from inner(),然后进入内层的generator,执行到yield total,此次代码执行结束,进入中断。

    当我们第二次启动生成器时,c.send(2)会将2直接传递给内层的input_val,然后执行内层的yield,打印出’break’,内层的yield结束,打印出’inner end’,由于此时是中断,没有yield,即没有返回值,那么yield from inner()的返回值为None,打印出a为None,最后打印结束语句,退出程序。

    此时我们可以看出,yield from会将send的值传递到最里层,再次启动生成器也是从最里层的yield开始启动,然后当内层的yield结束后执行外层的yield直到程序结束。我们可以画一个示例图帮助理解

这里写图片描述

3、async/await

    async和await是python3.5之后的新语法,可以用来替换@asyncio.coroutine和await。

    async可以将一个generator标记为coroutine类型,然后将其放入event_loop中执行,当我们在event_loop中放入多个coroutine的generator后,我们启动事件循环,此时遇到await,该generator就会中断,继续执行下一个generator,直到所有的generator的await执行完成后,event_loop再开始下一次消息循环,此时会再次启动event_loop中的生成器。

    举一个形象地例子就是你在快餐店排队,每一个人在收银台付款后(await money),到取餐处等待,直到每个人都付款之后,开始打包外卖,谁的先做好就可以拿走,而不是根据付款的顺序来取餐。

    关于异步IO在服务器编程方面用到的地方很多,了解了基本的特性后,还需要大量的实际编程经验才能真正掌握python中协程的概念,继续去填坑了0.0。。。

猜你喜欢

转载自blog.csdn.net/wingwc/article/details/78741236