Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it safe that when Two asyncio tasks access the same awaitable object?

Simply speaking, thread-safe means that it is safe when more than one thread access the same resource and I know Asyncio use a single thread fundamentally.

However, more than one Asyncio Task could access a resource multiple time at a time like multi-threading.

For example DB connection(if the object is not thread-safe and supports Asyncio operation).

  1. Schedule Task A and Task B accessing the same DB object.
  2. IO Loop executes Task A.
  3. Task A await IO operation on the DB object.(it will take long time enough)
  4. IO Loop executes Task B
  5. Step3's IO operation is still in progress(not done).
  6. Task B await IO operation on the same DB object.
  7. Now Task B is trying to access the same object at a time.

Is it completely safe in Asyncio and if so, what does it make safe?

like image 430
SangminKim Avatar asked Feb 24 '18 18:02

SangminKim


People also ask

Is Asyncio a concurrency?

What is asyncio? Asyncio stands for asynchronous input output and refers to a programming paradigm which achieves high concurrency using a single thread or event loop. The model isn't novel to Python and is implemented in other languages and frameworks too, the most prominent being JavaScript's NodeJS.

How many times should Asyncio run () be called?

How many times should Asyncio run () be called? It should be used as a main entry point for asyncio programs, and should ideally only be called once. New in version 3.7.

Does Asyncio use multithreading?

While the name of asyncio may make us think that this library is only good for I/O operations, it has functionality to handle other types of operations as well by interoperating with multithreading and multiprocessing.

Can you have multiple event loops in Python?

You can create multiple threads and run different event loops in each of them.


1 Answers

Using the same asyncio object from multiple tasks is safe in general. As an example, aiohttp has a session object, and it is expected for multiple tasks to access the same session "in parallel".

if so, what does it make safe?

The basic architecture of asyncio allows for multiple coroutines to await a single future result - they will simply all subscribe to the future's completion, and all will be scheduled to run once the result is ready. And this applies not only to coroutines, but also to synchronous code that subscribes to the future using add_done_callback.

That is how asyncio will handle your scenario: tasks A and B will ultimately subscribe to some future awaited by the DB object and. Once the result is available, it will be delivered to both of them, in turn.

Pitfalls typically associated with multi-threaded programming do not apply to asyncio because:

  • Unlike with threads, it is very predictable where a context switch can occur - just look at await statements in the code (and also async with and async for - but those are still very visible keywords). Anything between them is, for all intents and purposes, atomic. This eliminates the need for synchronization primitives to protect objects, as well as the mistakes that result from mishandling such tools.

  • All access to data happens from the thread that runs the event loop. This eliminates the possibility of a data race, reading of shared memory that is being concurrently written to.

One scenario in which multi-tasking could fail is multiple consumers attaching to the same stream-like resource. For example, if several tasks try to await reader.read(n) on the same reader stream, exactly one of them will get the new data1, and the others will keep waiting until new data arrives. The same applies to any shared streaming resource, including file descriptors or generators shared by multiple objects. And even then, one of the tasks is guaranteed to obtain the data, and the integrity of the stream object will not be compromised in any way.


1 One task receiving the data only applies if the tasks share the reader and each task separately calls data = await reader.read(n). If one were to extract a future with fut = asyncio.ensure_future(reader.read(n)) (without using await), share the future among multiple tasks, and await it in each task with data = await fut, all tasks would be notified of the particular chunk of data that ends up returned by that future.

like image 186
user4815162342 Avatar answered Sep 23 '22 03:09

user4815162342