Python3 协作多任务中的“await”是吗?

问题描述 投票:0回答:3

我正在尝试了解新的 asyncio 协程(在 Python 3.5 中引入)。

1997 年,我在大学参加了一门课程,大致涵盖了 Andrew Tanenbaum 所著的《现代操作系统》一书的内容。 不知何故,Python3 中的

await

让我想起了

协作多任务处理
来自维基百科:

协作多任务处理,也称为非抢占式多任务处理,是一种计算机多任务处理方式,其中操作系统从不启动从正在运行的进程到另一个进程的上下文切换。相反,进程会定期或在空闲时自愿放弃控制,以便使多个应用程序能够同时运行。这种类型的多任务处理称为“协作”,因为所有程序都必须协作才能使整个调度方案发挥作用。

如果将 Python 解释器视为操作系统,那么术语“协作多任务处理”是否适用于
await

但也许我错过了一些东西。

2023 年更新

我使用 Golang 已有一年了,我喜欢它。没有异步函数,没有 Promise。 Go 非常适合并发。

python asynchronous async-await python-asyncio
3个回答
20
投票
在协程函数内部,await 表达式可用于 暂停协程执行,直到结果可用。任何物体 可以等待,只要它实现了可等待协议 定义
await

() 方法。

协程可以使用另一个协程的await关键字来暂停执行。当它暂停时,协程的状态会被维持,从而允许它在下次被唤醒时从中断处恢复。对我来说,这听起来很像协作式多任务处理。请参阅此
示例


12
投票

用一个小程序来证明一下怎么样?首先让我们以合作

asyncio.sleep

的方式睡眠一秒钟,然后让我们以阻塞

time.sleep
的方式睡眠一秒钟。让我们打印一个线程 id、在协程中花费的时间以及任务的 id。

import threading import asyncio import time async def async_function(i): started = time.time() print("Id:", i, "ThreadId:", threading.get_ident()) await asyncio.sleep(1) time.sleep(1) print("Id:", i, "ThreadId:", threading.get_ident(), "Time:", time.time() - started) async def async_main(): await asyncio.gather( async_function(1), async_function(2), async_function(3) ) loop = asyncio.get_event_loop() loop.run_until_complete(async_main())

现在让我们尝试看看:

Id: 3 ThreadId: 140027884312320 Id: 2 ThreadId: 140027884312320 Id: 1 ThreadId: 140027884312320 Id: 3 ThreadId: 140027884312320 Time: 2.002575397491455 Id: 2 ThreadId: 140027884312320 Time: 3.0038201808929443 Id: 1 ThreadId: 140027884312320 Time: 4.00504469871521

正如预期的那样。仅在一个线程中执行。 
asyncio.sleep(1)

是非阻塞的,因此并发处理所有这些需要 1 秒。

time.sleep(1)
正在阻塞(它不合作),因此它会阻塞其余部分。 Id
1
等待 id
2
完成,而 id
2
等待 id
3
完成。

C# 也有 async/await,它也有协作多任务吗?

让我们在 C# 中尝试同样的事情:

using System; using System.Threading; using System.Threading.Tasks; namespace AsyncTest { class MainClass { private static async Task AsyncMethod(int id) { var started = DateTime.Now; Console.WriteLine("Id: {0} ThreadId: {1}", id, Thread.CurrentThread.ManagedThreadId); await Task.Delay(1000); Thread.Sleep(1000); Console.WriteLine("Id: {0} ThreadId: {1} Time: {2}", id, Thread.CurrentThread.ManagedThreadId, DateTime.Now - started); } private static async Task MainAsync() { await Task.WhenAll(AsyncMethod(1), AsyncMethod(2), AsyncMethod(3)); } public static void Main (string[] args) { MainAsync().Wait(); } } }

运行它并...

Id: 1 ThreadId: 1 Id: 2 ThreadId: 1 Id: 3 ThreadId: 1 Id: 2 ThreadId: 7 Time: 00:00:02.0147000 Id: 3 ThreadId: 8 Time: 00:00:02.0144560 Id: 1 ThreadId: 6 Time: 00:00:02.0878160

该死。等待之后线程不同。每个协程只需要 2 秒!怎么了?

没有什么问题。

与 Python 不同,C# 中的 async/await 结合了协作多任务和多线程。

Task.Delay(1000) 确实是非阻塞的,但当协程恢复时,它可以像示例中那样在完全不同的线程中恢复。由于协程在三个不同的线程中继续运行,因此

Thread.Sleep(1000)
并行地阻止了它们。

请注意,C# 中还有更多内容可以影响此行为(例如 SynchronizationContext),但这是一个不同的主题。


3
投票
维基百科

协程是计算机程序组件,通过允许在某些位置挂起和恢复执行的多个入口点来概括“非抢占式多任务处理”的子例程。

© www.soinside.com 2019 - 2024. All rights reserved.