Trio 翻译过来是三重奏的意思,它提供了更方便异步编程,是 asyncio 的更高级的封装。

它试图简化复杂的 asyncio 模块。使用起来比 asyncio 和 Twisted 要简单的同时,拥有其同样强大功能。这个项目还很年轻,还处于试验阶段但是整体设计是可靠的。作者鼓励大家去尝试使用,如果遇到问题可以在 git 上对他提 issue。同时作者还提供了一个在线聊天室更方便与其沟通:https://gitter.im/python-trio/general。

准备工作

  • 确保你的 Python版本在3.5以及以上。
  • 安装 triopython3 -m pip install --upgrade trio
  • import trio 运行是否有错误,没有错误可以往下进行了。

知识准备 Async 方法

使用 trio 也就意味着你需要一直写异步方法。

从外观上看异步方法和标准方法没什么区别只是前面多了个async。

“Async” 是“asynchronous”的简写,为了区别于异步函数,我们称标准函数为同步函数,从用户角度异步函数和同步函数有以下区别:

  1. 要调用异步函数,必须使用 await 关键字。 因此,不要写 regular_double(3),而是写 await async_double(3).
  2. 不能在同步函数里使用 await,否则会出错。
    句法错误:

但是在异步函数中,await 是允许的:

综上所述:作为一个用户,异步函数相对于常规函数的全部优势在于异步函数具有超能力:它们可以调用其他异步函数。

在异步函数中可以调用其他异步函数,但是凡事有始有终,第一个异步函数如何调用呢?

我们继续往下看

如何调用第一个异步函数

这里我们可以使用 trio.run 来调用第一个异步函数。
接下来让我们看看 trio 的其他功能

异步中的等待

这里使用了异步等待函数 trio.sleep,它的功能和同步函数中的 time.sleep()差不多,但是因为需要使用 await 调用,所以由前面的结论我们知道这是一个异步函数用的等待方法。

事实这个例子没有实际用处,我们用同步函数就可以实现这个简单的功能。这里主要是为了演示异步函数中通过 await 可以调用其他的异步函数。

异步函数调用的典型结构

不要忘了写 await

如果忘了写 await 会发生什么,我们看下面的这个例子

运行之后发现

报错了,错误类型是 RuntimeWarning,后面是说协程 sleep 没有使用 await。

我们打印下 trio.sleep(3) 看到如下内容,表示这是一个协程,也就是一个异步函数由前面的内容可知。

我们把上面的 trio.sleep(2 * x)改为 await trio.sleep(2 * x) 即可。

记住如果运行时警告:coroutine 'RuntimeWarning: coroutine '…' was never awaited',也就意味这有个地方你没有写await。

运行多个异步函数

如果 trio 只是使用 await trio.sleep 这样毫无意义的例子就没有什么价值,所以下面我们来 trio 的其他功能,运行多个异步函数。

内容比较多让我们一步一步分析,首先是定义了 child1 和 child2 两个异步函数,定义方法和我们上面说的差不多。

接下来,我们将 parent 定义为一个异步函数,它将同时调用 child1 和 child2

它通过使用神秘的 async with 语句来创建“nursery”,然后将 child1 和 child2 通过 nusery 方法的 start_soon 添加到 nursery 中。

下面我们来说说 async with,其实也很简单,我们知道再读文件时候我们使用with open()…去创建一个文件句柄,with里面牵扯到两个魔法函数

在代码块开始的时候调用enter()结束时再去调用exit()我们称open()为上下文管理器。async with someobj语句和with差不多只不过它调用的异步方法的魔法函数:aenteraexit。我们称someobj为“异步上下文管理器”。

再回到上面的代码首先我们使用 async with 创建一个异步代码块
同时通过 nursery.start_soon(child1) 和 nursery.start_soon(child2) 调用child1和child2函数开始运行然后立即返回,这两个异步函数留在后台继续运行。

然后等待 child1 和 child2 运行结束之后,结束 async with 代码块里的内容,打印最后的

"parent: all done!"。

让我们看看运行结果

可以发现和我们上面分析的一样。看到这里,如果你熟悉线程的话,你会发现这个运作机制和多线程类似。但是这里并不是线程,这里的代码全部在一个线程里面的完成,为了区别线程我们称这里的 child1 和 child2 为两个任务,有了任务,我们只能在某些我们称之为“checkpoints”的指定地点进行切换。后面我们再深挖掘它。

trio 里的跟踪器

我们知道上面的多个任务都是在一个线程中进行切换操作的,但是对于如何切换的我们并不了解,只有知道了这些我们才能更好的学好一个模块。
幸运的是,trio 提供了一组用于检查和调试程序的工具。我们可以通过编写一个 Tracer 类,来实现 trio.abc.Instrumen 接口。代码如下

然后我们运行之前的示例但是这次我们传入的是一个 Tracer 对象。

然后我们会发现打印了一大堆东西下面我们一部分一部分分析。

前面一大堆的信息我们不用去关心,我们看 ### new task spawned: main.parent,可知main.parent 创建了一个任务。

一旦初始的管理工作完成,trio 就开始运行 parent 函数,您可以看到 parent 函数创建了两个子任务。然后,它以块的形式到达异步的末尾,并暂停。

然后到了 trio.run(),记录了更多的内部运作过程。

然后给这两个子任务一个运行的机会

每个任务都在运行,直到调用 trio.sleep() 然后突然我们又回到 trio.run () 决定下一步要运行什么。这是怎么回事?秘密在于 trio.run () 和 trio.sleep () 一起实现的,trio.sleep() 可以获得一些特殊的魔力,让它暂停整个调用堆栈,所以它会向 trio.run () 发送一个通知,请求在1秒后再次被唤醒,然后暂停任务。任务暂停后,Python 将控制权交还给 trio.run (),由它决定下一步要做什么。

注意:在 trio 中不能使用 asyncio.sleep()。

接下来它调用一个操作系统原语来使整个进程进入休眠状态

1s休眠结束后

还记得 parent 是如何的等待两个子任务结束的么,下面注意观察 child1 退出的时候 parent 在干什么

然后先进行 io 操作,然后 parent 任务结束

最后进行一些内部操作代码结束

ok,这一部分只要说了运作机制了解即可,当然记住更方便对 trio 的理解。
关于更多的 trio 的使用,敬请期待。。。

参考资料

https://trio.readthedocs.io/en/latest/tutorial.html#before-you-begin
转自崔庆才博客




HTTPX 基础教程-新乡seo|网站优化,网站建设_微信公众号:zeropython—昊天博客