cpython/Doc/library/asyncio-sync.rst

336 lines
8.5 KiB
ReStructuredText

.. 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()
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()
.. 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()
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.
.. _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)
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.
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)
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.
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)
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*.
---------
.. versionchanged:: 3.9
Acquiring a lock using ``await lock`` or ``yield from lock`` and/or
:keyword:`with` statement (``with await lock``, ``with (yield from
lock)``) was removed. Use ``async with lock`` instead.