mirror of
https://github.com/LCTT/TranslateProject.git
synced 2024-12-23 21:20:42 +08:00
校对完毕
校对完毕
This commit is contained in:
parent
30808bb78c
commit
e3633468fd
@ -124,73 +124,72 @@ __main__:1: RuntimeWarning: coroutine 'foo' was never awaited
|
||||
|
||||
* 原生的协程
|
||||
* 配置了 `CO_ITERABLE_COROUTINE` 标识的生成器(文中有涉及)
|
||||
* 具有`__await__`方法的对象
|
||||
* 具有`__await__`方法的对象
|
||||
|
||||
|
||||
除了生成器由于遗留的原因不是使用`__await__`方法,其他的对象都使用。 `CO_ITERABLE_COROUTINE` 标志来自哪里? 它来自一个协程包装器(现在与 `sys.set_coroutine_wrapper` 有些混淆),即 `@asyncio.coroutine`。 通过一些间接方法,它使用types.coroutine(现在与types.CoroutineType或asyncio.coroutine有些混淆)包装生成器,并通过另外一个标志CO_ITERABLE_COROUTINE重新创建内部代码对象。
|
||||
除了生成器由于遗留的原因不是使用`__await__`方法,其他的对象都使用。 `CO_ITERABLE_COROUTINE` 标志来自哪里? 它来自一个协程包装器(现在与 `sys.set_coroutine_wrapper` 有些混淆),即 `@asyncio.coroutine`。 通过一些间接方法,它使用 types.coroutine(现在与 types.CoroutineType 或asyncio.coroutine 有些混淆)包装生成器,并通过另外一个标志 CO_ITERABLE_COROUTINE 重新创建内部代码对象。
|
||||
|
||||
It comes from a coroutine wrapper (now to be confused with sys.set_coroutine_wrapper) that is @asyncio.coroutine. That through some indirection will wrap the generator with types.coroutine (to to be confused with types.CoroutineType or asyncio.coroutine) which will re-create the internal code object with the additional flag CO_ITERABLE_COROUTINE.
|
||||
所以现在我们知道这些东西是什么,什么是future? 首先,我们需要澄清一件事情:在Python 3中,实际上有两种(完全不兼容)future类型:asyncio.futures.Future和concurrent.futures。 其中一个再现在另一个之前,但他们都仍然在asyncio使用。 例如,asyncio.run_coroutine_threadsafe()将调度一个协程到在另一个线程中运行的事件循环,但它返回一个concurrent.futures。Future对象而不是asyncio.futures。Future对象。 这是有道理的,因为只有concurrent.futures.Future对象是线程安全的。
|
||||
所以既然我们知道这些东西是什么,那么什么是 future? 首先,我们需要澄清一件事情:在 Python 3 中,实际上有两种(完全不兼容)的 future 类型:`asyncio.futures.Future` 和 `concurrent.futures.Future`。 其中一个出现在另一个之前,但他们都仍然在 asyncio 中使用。 例如,`asyncio.run_coroutine_threadsafe()` 将调度一个协程到在另一个线程中运行的事件循环,但它返回一个 `concurrent.futures.Future` 对象,而不是 `asyncio.futures.Future` 对象。 这是有道理的,因为只有 `concurrent.futures.Future` 对象是线程安全的。
|
||||
|
||||
所以现在我们知道有两个不兼容的future,我们应该澄清哪个future在asyncio中。 老实说,我不完全确定差异在哪里,但我打算暂时称之为“最终”。 它是一个最终将持有一个值的对象,你可以在最终结果仍然在计算时做一些处理。 future对象的一些变种称为deferred,还有一些叫做promise。 我实在难以理解它们真正的区别。
|
||||
所以现在我们知道有两个不兼容的 future,我们应该澄清哪个 future 在 asyncio 中。 老实说,我不完全确定差异在哪里,但我打算暂时称之为“最终”。 它是一个最终将持有一个值的对象,当还在计算时你可以对最终结果做一些处理。 future 对象的一些变种称为 deferred,还有一些叫做 promise。 我实在难以理解它们真正的区别。
|
||||
|
||||
你能用一个future对象做什么? 你可以关联一个准备就绪时将被调用的回调函数,或者你可以关联一个失败时将被触发的回调函数。 此外,你可以等待它(它实现await,因此可以等待),也可以取消它。
|
||||
你能用一个future对象做什么? 你可以关联一个准备就绪时将被调用的回调函数,或者你可以关联一个失败时将被触发的回调函数。 此外,你可以等待它(它实现`__await__`,因此可以等待),也可以取消它。
|
||||
|
||||
那么你怎样才能得到这样的future对象? 通过在await对象上调用asyncio.ensure_future。 它会把一个旧版的生成器转变为future对象。 然而,如果你阅读文档,你会读到asyncio.ensure_future实际上返回一个任务。 那么问题来了,什么是任务?
|
||||
那么你怎样才能得到这样的 future 对象? 通过在 await 对象上调用 `asyncio.ensure_future`。 它会把一个旧版的生成器转变为 future 对象。 然而,如果你阅读文档,你会读到 `asyncio.ensure_future` 实际上返回一个`task`(任务)。 那么问题来了,什么是任务?
|
||||
|
||||
### 任务
|
||||
|
||||
任务是一个包装协同程序的future对象。 它的工作方式和future类似,但它也有一些额外的方法来提取当前栈中包含的协同程序。 我们已经看到了前面提到的任务,因为它是通过Task.get_current确定事件循环当前正在做什么的主要方式。
|
||||
任务是一个包装协同程序的 future 对象。 它的工作方式和 future 类似,但它也有一些额外的方法来提取当前栈中包含的协同程序。 我们已经看到了前面提到的任务,因为它是通过 `Task.get_current` 确定事件循环当前正在做什么的主要方式。
|
||||
|
||||
在如何取消工作上任务和future也有区别,但这超出了本文的范围。 取消是它们自己最大的问题。 如果你在一个协程上,并且知道自己正在运行,你可以通过前面提到的Task.get_current获取自己的任务,但这需要你知道自己被派遣在哪个事件循环,该事件循环可能是也可能不是已绑定的线程。
|
||||
在如何取消工作上任务和 future 也有区别,但这超出了本文的范围。 取消是它们自己最大的问题。 如果你在一个协程上,并且知道自己正在运行,你可以通过前面提到的 `Task.get_current` 获取自己的任务,但这需要你知道自己被派遣在哪个事件循环,该事件循环可能是也可能不是已绑定的线程。
|
||||
|
||||
协程不可能知道它与哪个循环一起使用。任务也没有提供该信息公共API。 然而,如果你确实可以获得一个任务,你可以访问task._loop,通过它反指到事件循环。
|
||||
协程不可能知道它与哪个循环一起使用。*任务*也没有提供该信息公共 API。 然而,如果你确实可以获得一个任务,你可以访问 `task._loop`,通过它反指到事件循环。
|
||||
|
||||
### 句柄
|
||||
|
||||
除了上面提到的所有一切还有句柄。 句柄是等待执行的透明对象,不能等待,但可以被取消。 特别是如果你使用call_soon或者call_soon_threadsafe(还有其他一些)调度一个调用的执行,你可以通过句柄尽力尝试取消执行,但不能等待实际调用生效。
|
||||
除了上面提到的所有一切还有句柄。 句柄是等待执行的不透明对象,不能等待,但可以被取消。 特别是如果你使用 `call_soon` 或者 `call_soon_threadsafe`(还有其他一些)调度一个调用,你可以获得句柄,然后使用它尽力尝试取消执行,但不能等待实际调用生效。
|
||||
|
||||
### 执行器
|
||||
### <ruby>执行器<rt>Executors</rt></ruby>
|
||||
|
||||
因为你可以有多个事件循环,但这并不意味着每个线程理所当然地应用多个事件循环,最常见的情形还是一个线程一个事件循环。 那么你如何通知另一个事件循环做一些工作? 你不能到另一个线程的事件循环中执行回调函数并获取结果。 这种情况下,你需要使用执行器。
|
||||
|
||||
执行器来自concurrent.futures,它允许你将工作安排到本身未发生事件的线程中。 例如,如果在事件循环中使用run_in_executor来安编排将在另一个线程中调用的函数。 其返回结果是asyncio协程,而不是像run_coroutine_threadsafe这样的并发协同程序。 我还没有足够的心理能力来弄清楚为什么设计这样的API,应该如何使用,以及什么时候使用。 文档建议执行器可以用于构建多进程。
|
||||
执行器来自 `concurrent.futures`,它允许你将工作安排到本身未发生事件的线程中。 例如,如果在事件循环中使用 `run_in_executor` 来安编排将在另一个线程中调用的函数。 其返回结果是 asyncio 协程,而不是像 `run_coroutine_threadsafe` 这样的并发协同程序。 我还没有足够的心理能力来弄清楚为什么设计这样的 API,应该如何使用,以及什么时候使用。 文档建议执行器可以用于构建多进程。
|
||||
|
||||
### 传输和协议
|
||||
|
||||
我总是认为传输与协议也凌乱不堪,实际这部分内容基本上是对Twisted的逐字拷贝。详情毋庸赘述也,请直接阅读相关文档。
|
||||
我总是认为传输与协议也凌乱不堪,实际这部分内容基本上是对 Twisted 的逐字拷贝。详情毋庸赘述,请直接阅读相关文档。
|
||||
|
||||
### 如何使用asyncio
|
||||
### 如何使用 asyncio
|
||||
|
||||
现在我们已经大致了解asyncio,我发现了一些模式,人们似乎在使用asyncio代码时使用:
|
||||
现在我们已经大致了解 asyncio,我发现了一些模式,人们似乎在写 asyncio 代码时使用:
|
||||
|
||||
* 将事件循环传递给所有协程。 这似乎是社区中一部分人的做法。 把事件循环信息提供给协程为协程获取自己运行的任务提供了可能性。
|
||||
* 或者你要求事件循环绑定到线程,这也能达到同样的目的。 理想情况下两者都支持。 可悲的是,社区已经分化。
|
||||
* 如果想使用上下文数据(如线程本地数据),你可谓是运气不佳。 最流行的变通方法显然是atlassian的aiolocals,它基本上需要你手动传递上下文信息到协程,因为解释器不提供支持。 这意味着如果你有一个实用程序库生成协程,你将失去上下文。
|
||||
* 忽略Python中的旧协程。 只使用3.5版本中async def和co关键字。 你总可能要用到它们,因为在老版本中,没有异步上下文管理器,这是非常必要的资源管理。
|
||||
* 学习重新启动事件循环进行善后清理。 这部分功能和我预想的不同,我花了比较长的时间来厘清它的实现。清理操作的最好方式是不断重启事件循环直到没有等待事件。 遗憾的是没有什么通用的模式来处理清理操作,你只能用一些丑陋的临时方案糊口度日。 例如aiohttp的web支持也做这个模式,所以如果你想要结合两个清理逻辑,你可能需要重新实现它提供的实用程序助手,因为该助手功能实现后,它彻底破坏了事件循环的设计。 当然,它不是我见过的第一个干这种坏事的库:(
|
||||
* 使用子进程是不明显的。 你需要一个事件循环在主线程中运行,我想它是在监听信号事件,然后分派到其他事件循环。 这需要通过asyncio.get_child_watcher()、attach_loop(...)通知循环。
|
||||
* 编写同时支持异步和同步的代码在某种程度上注定要失败。 尝试在同一个对象上支持with和async with是危险的事情。
|
||||
* 如果你想给coroutine一个更好的名字,弄清楚为什么它没有被等待,设置name没有帮助。 你需要设置qualname。
|
||||
* 有时内部类型交换使你麻痹。 特别是asyncio.wait()函数将确保所有的事情都是future,这意味着如果你传递协程,你将很难发现你的协程是否已经完成或者正在等待,因为输入对象不再匹配输出对象 。 在这种情况下,唯一真正理智的做法是确保前期一切都是future。
|
||||
* 如果想使用上下文数据(如线程本地数据),你可谓是运气不佳。 最流行的变通方法显然是 atlassian 的 `aiolocals`,它基本上需要你手动传递上下文信息到协程,因为解释器不提供支持。 这意味着如果你有一个实用程序库生成协程,你将失去上下文。
|
||||
* 忽略 Python 中的旧协程。 只使用 3.5 版本中 `async def` 和 co 关键字。 你总可能要用到它们,因为在老版本中,没有异步上下文管理器,这是非常必要的资源管理。
|
||||
* 学习重新启动事件循环进行善后清理。 这部分功能和我预想的不同,我花了比较长的时间来厘清它的实现。清理操作的最好方式是不断重启事件循环直到没有等待事件。 遗憾的是没有什么通用的模式来处理清理操作,你只能用一些丑陋的临时方案糊口度日。 例如 aiohttp 的 web 支持也做这个模式,所以如果你想要结合两个清理逻辑,你可能需要重新实现它提供的实用程序助手,因为该助手功能实现后,它彻底破坏了事件循环的设计。 当然,它不是我见过的第一个干这种坏事的库:(。
|
||||
* 使用子进程是不明显的。 你需要一个事件循环在主线程中运行,我想它是在监听信号事件,然后分派到其他事件循环。 这需要通过 `asyncio.get_child_watcher().attach_loop(...)` 通知循环。
|
||||
* 编写同时支持异步和同步的代码在某种程度上注定要失败。 尝试在同一个对象上支持 `with` 和 `async with` 是危险的事情。
|
||||
* 如果你想给 coroutine 一个更好的名字,弄清楚为什么它没有被等待,设置 `__name__`没有帮助。 你需要设置 `__qualname__`。
|
||||
* 有时内部类型交换使你麻痹。 特别是 `asyncio.wait()` 函数将确保所有的事情都是 future,这意味着如果你传递协程,你将很难发现你的协程是否已经完成或者正在等待,因为输入对象不再匹配输出对象。 在这种情况下,唯一真正理智的做法是确保前期一切都是 future。
|
||||
|
||||
### 上下文数据
|
||||
|
||||
除了疯狂的复杂性和缺乏理解如何更好地编写API,我最大的问题是完全缺乏对上下文本地数据的考虑。这是Node社区现在学习的东西。continuation-local-storage存在,但实现太晚。连续本地存储和类似概念被定期用于在并发环境中实施安全策略,并且该信息的损坏可能导致严重的安全问题。
|
||||
除了疯狂的复杂性和缺乏理解如何更好地编写 API,我最大的问题是完全缺乏对上下文本地数据的考虑。这是 Node 社区现在学习的东西。`continuation-local-storage` 存在,但实现的太晚。连续本地存储和类似概念常用于在并发环境中实施安全策略,并且该信息的损坏可能导致严重的安全问题。
|
||||
|
||||
事实上,Python甚至没有任何存储,这令人失望至极。我正在研究这个内容,因为我正在调查如何最好地支持[Sentry's breadcrumbs] [3] 的asyncio,然而我并没有看到一个合理的方式做到这一点。在asyncio中没有上下文的概念,没有办法从通用代码中找出您正在使用的事件循环,并且如果没有monkeypatching(运行环境下的补丁),也无法获取这些信息。
|
||||
事实上,Python 甚至没有任何存储,这令人失望至极。我正在研究这个内容,因为我正在调查如何最好地支持 [Sentry's breadcrumbs] [3] 的 asyncio,然而我并没有看到一个合理的方式做到这一点。在 asyncio 中没有上下文的概念,没有办法从通用代码中找出您正在使用的事件循环,并且如果没有 monkeypatching(运行环境下的补丁),也无法获取这些信息。
|
||||
|
||||
Node当前正在经历如何[找到这个问题的长期解决方案] [2]的过程。这个问题不容忽视,因为它在所有生态系统中反复出现过,如JavaScript,Python和.NET环境。问题[被命名为异步上下文传播] [1],其解决方案有许多名称。在Go中,需要使用上下文包,并明确地传递给所有goroutine(不是一个完美的解决方案,但至少有一个)。 .NET具有本地调用上下文形式的最佳解决方案。它可以是线程上下文,Web请求上下文或类似的东西,除非被抑制,否则它会自动传播。微软的解决方案是我们的黄金标准。我现在相信,微软在15年前已经解决了该问题。
|
||||
Node 当前正在经历如何[找到这个问题的长期解决方案] [2]的过程。这个问题不容忽视,因为它在所有生态系统中反复出现过,如 JavaScript,Python 和 .NET 环境。问题[被命名为异步上下文传播] [1],其解决方案有许多名称。在 Go 中,需要使用上下文包,并明确地传递给所有 goroutine(不是一个完美的解决方案,但至少有一个)。.NET 具有本地调用上下文形式的最佳解决方案。它可以是线程上下文,Web 请求上下文或类似的东西,除非被抑制,否则它会自动传播。微软的解决方案是我们的黄金标准。我现在相信,微软在 15 年前已经解决了该问题。
|
||||
|
||||
我不知道生态系统是否还年轻,还可以添加逻辑调用上下文,可能现在仍然为时未晚。
|
||||
|
||||
### 个人感想
|
||||
|
||||
输出asynio的man帮助是复杂的,并且它变得越来越复杂。 我没有心理能力随便使用asyncio。 它需要不断地更新所有Python语言更新的知识,这很大程度上使语言本身变得复杂。 令人鼓舞的是,一个生态系统正围绕着它不断发展,只是不知道需要几年的时间,才能带给开发者愉快和稳定的开发体验。
|
||||
输出 asynio 的 man 帮助是复杂的,并且它变得越来越复杂。 我没有心理能力随便使用 asyncio。 它需要不断地更新所有 Python 语言的变化,这很大程度上使语言本身变得复杂。 令人鼓舞的是,一个生态系统正围绕着它不断发展,只是不知道还需要几年的时间,才能带给开发者愉快和稳定的开发体验。
|
||||
|
||||
3.5版本引入的东西(新的协程对象)非常棒。 特别是这些变化包括引入了一个合理的基础,这些都是我在早期的版本中一直期盼的。在我心中, 通过生成器实现协程是一个错误。 关于什么是asyncio,我难以置喙。 这是一个非常复杂的事情,内部令人眼花缭乱。 我很难理解它工作的所有细节。 你什么时候可以传递一个生成器,什么时候它必须是一个真正的协程,future是什么,任务是什么,事件循环如何工作,这甚至还没有触碰到真正的IO部分。
|
||||
3.5 版本引入的东西(新的协程对象)非常棒。 特别是这些变化包括引入了一个合理的基础,这些都是我在早期的版本中一直期盼的。在我心中, 通过生成器实现协程是一个错误。 关于什么是 asyncio,我难以置喙。 这是一个非常复杂的事情,内部令人眼花缭乱。 我很难理解它工作的所有细节。你什么时候可以传递一个生成器,什么时候它必须是一个真正的协程,future 是什么,任务是什么,事件循环如何工作,这甚至还没有触碰到真正的 IO 部分。
|
||||
|
||||
最糟糕的是,asyncio甚至不是特别快。 David Beazley演示的他设计的asyncio的替代品是原生版本速度的两倍。 asyncio巨复杂,很难理解,也无法兑现自己在主要特性上的承诺,对于它,我只想说我想静静。我知道,至少我对asyncio理解的不够透彻,没有足够的信心对人们如何构建代码给出建议。
|
||||
最糟糕的是,asyncio 甚至不是特别快。 David Beazley 演示的他设计的 asyncio 的替代品是原生版本速度的两倍。 asyncio 巨复杂,很难理解,也无法兑现自己在主要特性上的承诺,对于它,我只想说我想静静。我知道,至少我对 asyncio 理解的不够透彻,没有足够的信心对人们为它如何构建代码给出建议。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user