在Python3合作多任务中是否"等待"?

gue*_*tli 5 python asynchronous async-await python-asyncio

我试图了解新的异步协同程序(在Python 3.5中引入).

1997年,我参加了大学课程,大致涵盖了Andrew Tanenbaum 所着的" 现代操作系统 "一书的内容.

不知何故await,Python3让我想起了Cooperative Multitasking.

来自维基百科:

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

如果你像操作系统一样看Python解释器,"合作多任务"一词是否适用于await

但可能是我错过了一些东西.

Ton*_*ent 7

在coroutine函数中,await表达式可用于暂停协程执行,直到结果可用.只要通过定义await()方法实现等待协议,就可以等待任何对象.

协程可以使用await关键字与另一个协程暂停执行.当它被暂停时,协程的状态被保持,允许它在下次被唤醒时从它停止的地方恢复.这对我来说听起来很像Cooperative多任务处理.看这个例子

  • 我认为事实上我们可以通过使用 `await asyncio.sleep(0)` 放弃对事件循环的控制(例如,在长时间运行的函数中,请参阅[github上的讨论](https://github.com/例如 python/asyncio/issues/284)也使它听起来像是协作式多任务处理。 (2认同)

Sta*_*kop 6

确实是合作多任务.

怎样一个小程序来证明它.让我们首先与合作者睡一会儿,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())
Run Code Online (Sandbox Code Playgroud)

现在让我们试着看看:

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
Run Code Online (Sandbox Code Playgroud)

正如所料.执行只在一个线程中.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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

跑吧......

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
Run Code Online (Sandbox Code Playgroud)

该死的.等待之后线程不同.每个协程只花了2秒钟!怎么了?

没有错误.与Python不同,C#中的async/await具有协作式多任务和多线程的组合. Task.Delay(1000)确实是非阻塞的,但是当协程恢复时,它可以恢复到与示例中完全不同的线程.由于协同程序在三个不同的线程中继续Thread.Sleep(1000),因此并行阻止它们.

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