2

A style of programming in which task release the CPU during waiting periods, so that other tasks can use it.

To introduce an async task,

  • A task should be capable to suspend & resume
  • An event scheduler should schedule those tasks(when ready)

Wrote event scheduler, but stuck in enabling an async task. Assume the task being IO bound. Scheduler does not get CPU slice, amidst task execution, because task gets executed synchronously.


A task(bar) in python, becomes async task(capable to suspend & resume), when task uses async, await keywords with ayncio.onSomeIO capable wait,

async def bar(): await asyncio.onSomeIO() 

Question:

How asyncio package enables bar, to be an async task, with these keywords, under the hood? Does each task get launched on separate thread?

1
  • 3
    You might want to look at the following article from Brett Cannon about what is under the hood for async/await. It's very in depth, but it's not a topic that could be covered easily on S/O. Commented Aug 14, 2017 at 4:21

1 Answer 1

4

Does each task get launched on separate thread?

No, usually asyncio runs in single thread.

How asyncio package enables bar, to be an async task, with these keywords, under the hood?

When you define function as async this function becomes generator what allows to execute it "by steps" using __next__() method. await - is yield (yield from actually) point where execution flow returns to global event loop that manages executing of all coroutines.

This simple example shows how you can switch between execution flow of different generators:

def task(i): yield 1 print('task {}: step 1'.format(i)) yield 2 print('task {}: step 2'.format(i)) tasks = [ task(1), task(2), task(3), ] def execute_tasks(tasks): i = 0 finished = [] while True: # start executing tasks: try: tasks[i].__next__() except StopIteration: finished.append(i) # check if any task unfinished: if len(finished) == len(tasks): return # move to next unfinished task: while True: i += 1 if i > len(tasks) - 1: i = 0 if not i in finished: break if __name__ == '__main__': execute_tasks(tasks) 

Output:

task 1: step 1 task 2: step 1 task 3: step 1 task 1: step 2 task 2: step 2 task 3: step 2 

asyncio of course is much more complex and allows you much more.

Probably best explanation of how you can implement coroutines using generators I saw in this PyCon 2015 video: David Beazley - Python Concurrency From the Ground Up: LIVE! (source code). You should definitely watch it if you're going implement this.

But I advice you to use asyncio instead - it already exists for you, there's no need to invent your own.

Sign up to request clarification or add additional context in comments.

3 Comments

Instead of yield 1 can we request for some IO bound operation? I have this code here without event-loop
@overexchange try to look at code from video I gave link to above (I added link to code there). run() there - is actually event loop. 1) It tells tasks to execute until I/O operations (line 43) 2) tasks yield on I/O operations to return control to event loop (lines 82, 88, 94, 96) 3) when event loop see all tasks pending it uses select to wait some I/O done (line 33).
Pycon presentation is amazing. Not sure, how did I miss?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.