0
#!/usr/bin/python3
# -*- coding: utf-8 -*-

import asyncio,time

async def asleep(n):
    time.sleep(n)   # blocking method


async def doAsync(n):                        
    print("doAsync{} start...".format(n))
    await asleep(2)                             # case 1    
    #await asyncio.gather(asleep(2))            # case 2
    print("doAsync{} end...".format(n))
    #...                                     

tasks = [doAsync(i) for i in range(1,4)]
tasks = [asyncio.ensure_future(t) for t in tasks]


if __name__ == "__main__":                  
    loop = asyncio.get_event_loop()         
    loop.run_until_complete(asyncio.wait(tasks))
    loop.close()                            

output:

## case 1 output        # No concurrency runs
doAsync1 start...       
(wait 2 seconds)
doAsync1 end...
doAsync2 start...
(wait 2 seconds)
doAsync2 end...
doAsync3 start...
(wait 2 seconds)
doAsync3 end...

## case 2 output        # Concurrency runs
doAsync1 start...
doAsync2 start...
doAsync3 start...
(wait 2*3 seconds)      
doAsync1 end...
doAsync2 end...
doAsync3 end...

case 1:

Although asleep() is also a coroutine, But the doAsync isn't pending at "await asleep()"
doAsync does not implement concurrency

case 2:

doAsync is concurrent

The question:

  1. How to explain how it works detail.
  2. When should I use "await some_work()" and when should I use "await asyncio.gather(some_work)"
leo
  • 33
  • 4

1 Answers1

0

Code you provided is not concurrent in both cases. It happens because of this line:

async def asleep(n):
    time.sleep(n)   # blocking method

time.sleep freezes asyncio event loop making it unable to process other running same time coroutines. You should never do such thing when using asyncio (if you want to run some blocking code inside coroutine, you should run it in another thread using loop.run_in_executor as shown here).

To make your code coroutine working properly you should use special asyncio version of sleep that doesn't block event loop:

async def asleep(n):
    await asyncio.sleep(n)   # blocking method

Compare executing this version (in case1) with version you posted (in case1): 6 seconds vs. 2 seconds.


Once we creating tasks from coroutines using ensure_future these coroutines start running concurently:

tasks = [asyncio.ensure_future(c) for c in coros]  # this run all coroutines concurently

Using asyncio.gather - is alternative way to run coroutines concurently:

await asyncio.gather(*coros)  # this run all coroutines concurently

Way asyncio works in detail may seem pretty complex. Best explanation I've seen: David Beazley - Python Concurrency From the Ground Up. Note, that you usually don't need to understand low-level detail to use asyncio on practice.

Mikhail Gerasimov
  • 36,989
  • 16
  • 116
  • 159
  • Thank you very much I think my problem may not be concurrency and asynchrony, but the scheduling problem of the coroutine – leo Jan 06 '18 at 04:45