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

时间:2016-08-10 05:39:52

标签: python asynchronous async-await python-asyncio

我正在尝试理解新的async协同程序(在Python 3.5中引入)。

1997年,我参加了大学课程,大致涵盖了Andrew Tanenbaum撰写的Modern Operating Systems书的内容。

不知何故,Python3中的awaitCooperative Multitasking提醒我。

来自维基百科:

  

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

如果你像操作系统一样看Python解释器,那么术语"合作多任务"适用于await

但可能是我错过了什么。

3 个答案:

答案 0 :(得分:7)

  

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

协程可以使用await关键字与另一个协程暂停执行。当它被暂停时,协程的状态被保持,允许它在下次被唤醒时从它停止的地方恢复。 这听起来很像我的合作多任务。见example

答案 1 :(得分: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())

现在让我们试试看:

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)正在阻塞(它不合作),所以它会阻止其余的。当1等待身份2完成时,身份2等待身份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),但这是一个不同的主题。

答案 2 :(得分:3)

是。根据{{​​3}}:

  

协同程序是计算机程序组件,它通过允许多个入口点在某些位置暂停和恢复执行来概括非抢占式多任务的子程序。