Asyncio First Look

  |   Source

Definitions

  • A Good explanation for Threading
  • "A piece of code is thread-safe if it functions correctly during simultaneous execution by multiple threads." (Wikipedia). For a discussion about how thread-safety is strictly connected to scenarios in which specific objectives are pursued (accessing/modifying mutable data-structures for example), see here. The author sustains that thread-safety is strictly related to the tasks the software performs. It can be, or not, thread-safe related to what it is performing, and not just generically thread-safe.

Generators coroutines and async coroutines

from Pydocs:

"Coroutines used with asyncio may be implemented using the async def statement, or by using generators."

"Generator-based coroutines use the yield from syntax introduced in PEP 380, instead of the original yield syntax."

Disambiguation
  • The function that defines a coroutine (a function definition using async def or decorated with @asyncio.coroutine). If disambiguation is needed we will call this a coroutine function (iscoroutinefunction() returns True).

  • The object obtained by calling a coroutine function. This object represents a computation or an I/O operation (usually a combination) that will complete eventually. If disambiguation is needed we will call it a coroutine object (iscoroutine() returns True).

Basics

"Calling a coroutine does not start its code running. Calling a coroutine does not start its code running – the coroutine object returned by the call doesn’t do anything until you schedule its execution."

Things a coroutine can do:

result = await future or result = yield from future – suspends the coroutine until the future is done, then returns the future’s result, or raises an exception, which will be propagated. (If the future is cancelled, it will raise a CancelledError exception.) Note that tasks are futures, and everything said about futures also applies to tasks.

result = await coroutine or result = yield from coroutine – wait for another coroutine to produce a result (or raise an exception, which will be propagated). The coroutine expression must be a call to another coroutine.

return expression – produce a result to the coroutine that is waiting for this one using await or yield from.

raise exception – raise an exception in the coroutine that is waiting for this one using await or yield from.

Example: Two async coroutines
import asyncio

async def compute(x, y):
    print("Compute %s + %s ..." % (x, y))
    await asyncio.sleep(1.0)
    return x + y

async def print_sum(x, y):
    result = await compute(x, y)
    print("%s + %s = %s" % (x, y, result))

loop = asyncio.get_event_loop()
loop.run_until_complete(print_sum(1, 2))
loop.close()
  • await compute makes print_sum to wait for the coroutine to complete.

  • The loop is created by the BaseEventLoop.run_until_complete() method when it gets a coroutine object instead of a task.

  • see BaseEventLoop

Future

Construct used for synchronization in concurrent operations. "... describe an object that acts as a proxy for a result that is initially unknown, usually because the computation of its value is yet incomplete."(Wikipedia)

Return a result or an exception when it's done. If invoked can raise different exceptions defining its actual state (None, InvalidStateError, CancelledError). Can have a callback to be run when it's done. "result() and exception() methods do not take a timeout argument and raise an exception when the future isn’t done yet."

Task

"A task is responsible for executing a coroutine object in an event loop. If the wrapped coroutine yields from a future, the task suspends the execution of the wrapped coroutine and waits for the completition of the future. When the future is done, the execution of the wrapped coroutine restarts with the result or the exception of the future."

"Event loops use cooperative scheduling: an event loop only runs one task at a time. Other tasks may run in parallel if other event loops are running in different threads. While a task waits for the completion of a future, the event loop executes a new task."

Important: "Don’t directly create Task instances: use the ensure_future() function or the BaseEventLoop.create_task() method."