Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is asyncio.Future incompatible with concurrent.futures.Future?

The two classes represent excellent abstractions for concurrent programming, so it's a bit disconcerting that they don't support the same API.

Specifically, according to the docs:

asyncio.Future is almost compatible with concurrent.futures.Future.

Differences:

  • result() and exception() do not take a timeout argument and raise an exception when the future isn’t done yet.
  • Callbacks registered with add_done_callback() are always called via the event loop's call_soon_threadsafe().
  • This class is not compatible with the wait() and as_completed() functions in the concurrent.futures package.

The above list is actually incomplete, there are a couple more differences:

  • running() method is absent
  • result() and exception() may raise InvalidStateError if called too early

Are any of these due to the inherent nature of an event loop that makes these operations either useless or too troublesome to implement?

And what is the meaning of the difference related to add_done_callback()? Either way, the callback is guaranteed to happen at some unspecified time after the futures is done, so isn't it perfectly consistent between the two classes?

like image 202
max Avatar asked May 10 '17 01:05

max


People also ask

What is an Asyncio future?

Future Object. class asyncio. Future (*, loop=None) A Future represents an eventual result of an asynchronous operation.

What are Python futures?

futures. This module was added in Python 3.2 for providing the developers a high-level interface for launching asynchronous tasks. It is an abstraction layer on the top of Python's threading and multiprocessing modules for providing the interface for running the tasks using pool of thread or processes.

What is Awaitable Python?

A coroutine object is “awaitable” (it can be used in an await statement). Recall that when you are executing asynchronous code you are always doing so in the context of a “Task”, which is an object maintained by the Event Loop, and that each Task has its own call stack.

What does future pending mean in Python?

The future remains "pending" because the callback that would update is supposed to be called by the event loop, which is currently not operational - it just sits in a queue. Replacing time. sleep(0.1) with await asyncio.


1 Answers

The core reason for the difference is in how threads (and processes) handle blocks vs how coroutines handle events that block. In threading, the current thread is suspended until whatever condition resolves and the thread can go forward. For example in the case of the futures, if you request the result of a future, it's fine to suspend the current thread until that result is available.

However the concurrency model of an event loop is that rather than suspending code, you return to the event loop and get called again when ready. So it is an error to request the result of an asyncio future that doesn't have a result ready.

You might think that the asyncio future could just wait and while that would be inefficient, would it really be all that bad for your coroutine to block? It turns out though that having the coroutine block is very likely to mean that the future never completes. It is very likely that the future's result will be set by some code associated with the event loop running the code that requests the result. If the thread running that event loop blocks, no code associated with the event loop would run. So blocking on the result would deadlock and prevent the result from being produced.

So, yes, the differences in interface are due to this inherent difference. As an example, you wouldn't want to use an asyncio future with the concurrent.futures waiter abstraction because again that would block the event loop thread.

The add_done_callbacks difference guarantees that callbacks will be run in the event loop. That's desirable because they will get the event loop's thread local data. Also, a lot of coroutine code assumes that it will never be run at the same time as other code from the same event loop. That is, coroutines are only thread safe under the assumption that two coroutines from the same event loop do not run at the same time. Running the callbacks in the event loop avoids a lot of thread safety issues and makes it easier to write correct code.

like image 62
Sam Hartman Avatar answered Oct 04 '22 00:10

Sam Hartman