.. currentmodule:: asyncio .. _asyncio-sync: ========================== Synchronization Primitives ========================== **Source code:** :source:`Lib/asyncio/locks.py` ----------------------------------------------- asyncio synchronization primitives are designed to be similar to those of the :mod:`threading` module with two important caveats: * asyncio primitives are not thread-safe, therefore they should not be used for OS thread synchronization (use :mod:`threading` for that); * methods of these synchronization primitives do not accept the *timeout* argument; use the :func:`asyncio.wait_for` function to perform operations with timeouts. asyncio has the following basic synchronization primitives: * :class:`Lock` * :class:`Event` * :class:`Condition` * :class:`Semaphore` * :class:`BoundedSemaphore` --------- Lock ==== .. class:: Lock(\*, loop=None) Implements a mutex lock for asyncio tasks. Not thread-safe. An asyncio lock can be used to guarantee exclusive access to a shared resource. The preferred way to use a Lock is an :keyword:`async with` statement:: lock = asyncio.Lock() # ... later async with lock: # access shared state which is equivalent to:: lock = asyncio.Lock() # ... later await lock.acquire() try: # access shared state finally: lock.release() .. deprecated-removed:: 3.8 3.10 The *loop* parameter. .. coroutinemethod:: acquire() Acquire the lock. This method waits until the lock is *unlocked*, sets it to *locked* and returns ``True``. When more than one coroutine is blocked in :meth:`acquire` waiting for the lock to be unlocked, only one coroutine eventually proceeds. Acquiring a lock is *fair*: the coroutine that proceeds will be the first coroutine that started waiting on the lock. .. method:: release() Release the lock. When the lock is *locked*, reset it to *unlocked* and return. If the lock is *unlocked*, a :exc:`RuntimeError` is raised. .. method:: locked() Return ``True`` if the lock is *locked*. Event ===== .. class:: Event(\*, loop=None) An event object. Not thread-safe. An asyncio event can be used to notify multiple asyncio tasks that some event has happened. An Event object manages an internal flag that can be set to *true* with the :meth:`set` method and reset to *false* with the :meth:`clear` method. The :meth:`wait` method blocks until the flag is set to *true*. The flag is set to *false* initially. .. deprecated-removed:: 3.8 3.10 The *loop* parameter. .. _asyncio_example_sync_event: Example:: async def waiter(event): print('waiting for it ...') await event.wait() print('... got it!') async def main(): # Create an Event object. event = asyncio.Event() # Spawn a Task to wait until 'event' is set. waiter_task = asyncio.create_task(waiter(event)) # Sleep for 1 second and set the event. await asyncio.sleep(1) event.set() # Wait until the waiter task is finished. await waiter_task asyncio.run(main()) .. coroutinemethod:: wait() Wait until the event is set. If the event is set, return ``True`` immediately. Otherwise block until another task calls :meth:`set`. .. method:: set() Set the event. All tasks waiting for event to be set will be immediately awakened. .. method:: clear() Clear (unset) the event. Tasks awaiting on :meth:`wait` will now block until the :meth:`set` method is called again. .. method:: is_set() Return ``True`` if the event is set. Condition ========= .. class:: Condition(lock=None, \*, loop=None) A Condition object. Not thread-safe. An asyncio condition primitive can be used by a task to wait for some event to happen and then get exclusive access to a shared resource. In essence, a Condition object combines the functionality of an :class:`Event` and a :class:`Lock`. It is possible to have multiple Condition objects share one Lock, which allows coordinating exclusive access to a shared resource between different tasks interested in particular states of that shared resource. The optional *lock* argument must be a :class:`Lock` object or ``None``. In the latter case a new Lock object is created automatically. .. deprecated-removed:: 3.8 3.10 The *loop* parameter. The preferred way to use a Condition is an :keyword:`async with` statement:: cond = asyncio.Condition() # ... later async with cond: await cond.wait() which is equivalent to:: cond = asyncio.Condition() # ... later await cond.acquire() try: await cond.wait() finally: cond.release() .. coroutinemethod:: acquire() Acquire the underlying lock. This method waits until the underlying lock is *unlocked*, sets it to *locked* and returns ``True``. .. method:: notify(n=1) Wake up at most *n* tasks (1 by default) waiting on this condition. The method is no-op if no tasks are waiting. The lock must be acquired before this method is called and released shortly after. If called with an *unlocked* lock a :exc:`RuntimeError` error is raised. .. method:: locked() Return ``True`` if the underlying lock is acquired. .. method:: notify_all() Wake up all tasks waiting on this condition. This method acts like :meth:`notify`, but wakes up all waiting tasks. The lock must be acquired before this method is called and released shortly after. If called with an *unlocked* lock a :exc:`RuntimeError` error is raised. .. method:: release() Release the underlying lock. When invoked on an unlocked lock, a :exc:`RuntimeError` is raised. .. coroutinemethod:: wait() Wait until notified. If the calling task has not acquired the lock when this method is called, a :exc:`RuntimeError` is raised. This method releases the underlying lock, and then blocks until it is awakened by a :meth:`notify` or :meth:`notify_all` call. Once awakened, the Condition re-acquires its lock and this method returns ``True``. .. coroutinemethod:: wait_for(predicate) Wait until a predicate becomes *true*. The predicate must be a callable which result will be interpreted as a boolean value. The final value is the return value. Semaphore ========= .. class:: Semaphore(value=1, \*, loop=None) A Semaphore object. Not thread-safe. A semaphore manages an internal counter which is decremented by each :meth:`acquire` call and incremented by each :meth:`release` call. The counter can never go below zero; when :meth:`acquire` finds that it is zero, it blocks, waiting until some task calls :meth:`release`. The optional *value* argument gives the initial value for the internal counter (``1`` by default). If the given value is less than ``0`` a :exc:`ValueError` is raised. .. deprecated-removed:: 3.8 3.10 The *loop* parameter. The preferred way to use a Semaphore is an :keyword:`async with` statement:: sem = asyncio.Semaphore(10) # ... later async with sem: # work with shared resource which is equivalent to:: sem = asyncio.Semaphore(10) # ... later await sem.acquire() try: # work with shared resource finally: sem.release() .. coroutinemethod:: acquire() Acquire a semaphore. If the internal counter is greater than zero, decrement it by one and return ``True`` immediately. If it is zero, wait until a :meth:`release` is called and return ``True``. .. method:: locked() Returns ``True`` if semaphore can not be acquired immediately. .. method:: release() Release a semaphore, incrementing the internal counter by one. Can wake up a task waiting to acquire the semaphore. Unlike :class:`BoundedSemaphore`, :class:`Semaphore` allows making more ``release()`` calls than ``acquire()`` calls. BoundedSemaphore ================ .. class:: BoundedSemaphore(value=1, \*, loop=None) A bounded semaphore object. Not thread-safe. Bounded Semaphore is a version of :class:`Semaphore` that raises a :exc:`ValueError` in :meth:`~Semaphore.release` if it increases the internal counter above the initial *value*. .. deprecated-removed:: 3.8 3.10 The *loop* parameter. --------- .. deprecated:: 3.7 Acquiring a lock using ``await lock`` or ``yield from lock`` and/or :keyword:`with` statement (``with await lock``, ``with (yield from lock)``) is deprecated. Use ``async with lock`` instead.