Is `await` in Python3 Cooperative Multitasking?

前端 未结 3 728
春和景丽
春和景丽 2021-01-18 06:08

I am trying to understand the new asyncio coroutines (introduced in Python 3.5).

In 1997 I attended a course at university which roughly covered the content of the

相关标签:
3条回答
  • 2021-01-18 06:36

    Inside a coroutine function, the await expression can be used to suspend coroutine execution until the result is available. Any object can be awaited, as long as it implements the awaitable protocol by defining the await() method.

    A coroutine can pause execution using the await keyword with another coroutine. While it is paused, the coroutine’s state is maintained, allowing it to resume where it left off the next time it is awakened. That sounds quite like Cooperative multitasking to me. See this example

    0 讨论(0)
  • 2021-01-18 06:37

    Yes. According to Wikipedia:

    Coroutines are computer program components that generalize subroutines for nonpreemptive multitasking, by allowing multiple entry points for suspending and resuming execution at certain locations.

    0 讨论(0)
  • 2021-01-18 06:52

    It is cooperative multitasking indeed.

    What about a small program to prove it. Let's first sleep with cooperative asyncio.sleep for a second and then let's sleep with blocking time.sleep for a second. Let's print a thread id, time spent in the coroutine and id of a task.

    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())
    

    Now let's try and see:

    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
    

    As expected. Execution was only in one thread. asyncio.sleep(1) is nonblocking, so it took 1 second to process all of them concurrently. time.sleep(1) is blocking (it does not cooperate), so it blocks the rest. Id 1 waits for id 2 to finish while id 2 waits for id 3 to finish.

    C# has async/await too, does it have cooperative multitasking as well?

    Let's try the same thing in 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 it and...

    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
    

    Damn. The threads are different after await. And it tooks just 2 seconds for each of the coroutine! What's wrong?

    Nothing is wrong. Unlike Python, async/await in C# has a combination of cooperative multitasking and multithreading. Task.Delay(1000) is indeed nonblocking but when a coroutine resumes, it can resume in a totally different thread as it did in the example. Since the coroutines continued in three different threads, Thread.Sleep(1000) blocked them in parallel.

    Note there are more things in C# which can influence this behavior (like SynchronizationContext), but this is a different topic.

    0 讨论(0)
提交回复
热议问题