Back to Kbengine

Synchronization Primitives

kbe/src/lib/python/Doc/library/asyncio-sync.rst

2.5.128.2 KB
Original Source

.. currentmodule:: asyncio

.. _asyncio-sync:

========================== Synchronization Primitives

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 sychronization 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()

.. coroutinemethod:: acquire()

  Acquire the lock.

  This method waits until the lock is *unlocked*, sets it to
  *locked* and returns ``True``.

.. 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.

.. _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.

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 lock.acquire()
   try:
       await cond.wait()
   finally:
       lock.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.

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:: 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.