Commit 126aef76 authored by Antoine Pitrou's avatar Antoine Pitrou

Issue #8799: Fix and improve the threading.Condition documentation.

parent 88b957a0
...@@ -503,63 +503,74 @@ Condition Objects ...@@ -503,63 +503,74 @@ Condition Objects
----------------- -----------------
A condition variable is always associated with some kind of lock; this can be A condition variable is always associated with some kind of lock; this can be
passed in or one will be created by default. (Passing one in is useful when passed in or one will be created by default. Passing one in is useful when
several condition variables must share the same lock.) several condition variables must share the same lock. The lock is part of
the condition object: you don't have to track it separately.
A condition variable has :meth:`acquire` and :meth:`release` methods that call
the corresponding methods of the associated lock. It also has a :meth:`wait` A condition variable obeys the :term:`context manager` protocol: using the
method, and :meth:`notify` and :meth:`notify_all` methods. These three must only ``with`` statement acquires the associated lock for the duration of the
be called when the calling thread has acquired the lock, otherwise a enclosed block. The :meth:`~Condition.acquire` and :meth:`~Condition.release`
:exc:`RuntimeError` is raised. methods also call the corresponding methods of the associated lock.
The :meth:`wait` method releases the lock, and then blocks until it is awakened Other methods must be called with the associated lock held. The
by a :meth:`notify` or :meth:`notify_all` call for the same condition variable in :meth:`~Condition.wait` method releases the lock, and then blocks until
another thread. Once awakened, it re-acquires the lock and returns. It is also another thread awakens it by calling :meth:`~Condition.notify` or
possible to specify a timeout. :meth:`~Condition.notify_all`. Once awakened, :meth:`~Condition.wait`
re-acquires the lock and returns. It is also possible to specify a timeout.
The :meth:`notify` method wakes up one of the threads waiting for the condition
variable, if any are waiting. The :meth:`notify_all` method wakes up all threads The :meth:`~Condition.notify` method wakes up one of the threads waiting for
waiting for the condition variable. the condition variable, if any are waiting. The :meth:`~Condition.notify_all`
method wakes up all threads waiting for the condition variable.
Note: the :meth:`notify` and :meth:`notify_all` methods don't release the lock;
this means that the thread or threads awakened will not return from their Note: the :meth:`~Condition.notify` and :meth:`~Condition.notify_all` methods
:meth:`wait` call immediately, but only when the thread that called don't release the lock; this means that the thread or threads awakened will
:meth:`notify` or :meth:`notify_all` finally relinquishes ownership of the lock. not return from their :meth:`~Condition.wait` call immediately, but only when
the thread that called :meth:`~Condition.notify` or :meth:`~Condition.notify_all`
Tip: the typical programming style using condition variables uses the lock to finally relinquishes ownership of the lock.
Usage
^^^^^
The typical programming style using condition variables uses the lock to
synchronize access to some shared state; threads that are interested in a synchronize access to some shared state; threads that are interested in a
particular change of state call :meth:`wait` repeatedly until they see the particular change of state call :meth:`~Condition.wait` repeatedly until they
desired state, while threads that modify the state call :meth:`notify` or see the desired state, while threads that modify the state call
:meth:`notify_all` when they change the state in such a way that it could :meth:`~Condition.notify` or :meth:`~Condition.notify_all` when they change
possibly be a desired state for one of the waiters. For example, the following the state in such a way that it could possibly be a desired state for one
code is a generic producer-consumer situation with unlimited buffer capacity:: of the waiters. For example, the following code is a generic
producer-consumer situation with unlimited buffer capacity::
# Consume one item # Consume one item
cv.acquire() with cv:
while not an_item_is_available(): while not an_item_is_available():
cv.wait() cv.wait()
get_an_available_item() get_an_available_item()
cv.release()
# Produce one item # Produce one item
cv.acquire() with cv:
make_an_item_available() make_an_item_available()
cv.notify()
cv.release()
To choose between :meth:`notify` and :meth:`notify_all`, consider whether one The ``while`` loop checking for the application's condition is necessary
state change can be interesting for only one or several waiting threads. E.g. because :meth:`~Condition.wait` can return after an arbitrary long time,
in a typical producer-consumer situation, adding one item to the buffer only and other threads may have exhausted the available items in between. This
needs to wake up one consumer thread. is inherent to multi-threaded programming. The :meth:`~Condition.wait_for`
method can be used to automate the condition checking::
Note: Condition variables can be, depending on the implementation, subject # Consume an item
to both spurious wakeups (when :meth:`wait` returns without a :meth:`notify` with cv:
call) and stolen wakeups (when another thread acquires the lock before the cv.wait_for(an_item_is_available)
awoken thread.) For this reason, it is always necessary to verify the state get_an_available_item()
the thread is waiting for when :meth:`wait` returns and optionally repeat
the call as often as necessary. To choose between :meth:`~Condition.notify` and :meth:`~Condition.notify_all`,
consider whether one state change can be interesting for only one or several
waiting threads. E.g. in a typical producer-consumer situation, adding one
item to the buffer only needs to wake up one consumer thread.
Interface
^^^^^^^^^
.. class:: Condition(lock=None) .. class:: Condition(lock=None)
If the *lock* argument is given and not ``None``, it must be a :class:`Lock` If the *lock* argument is given and not ``None``, it must be a :class:`Lock`
...@@ -626,12 +637,6 @@ the call as often as necessary. ...@@ -626,12 +637,6 @@ the call as often as necessary.
held when called and is re-aquired on return. The predicate is evaluated held when called and is re-aquired on return. The predicate is evaluated
with the lock held. with the lock held.
Using this method, the consumer example above can be written thus::
with cv:
cv.wait_for(an_item_is_available)
get_an_available_item()
.. versionadded:: 3.2 .. versionadded:: 3.2
.. method:: notify(n=1) .. method:: notify(n=1)
......
...@@ -138,6 +138,11 @@ Build ...@@ -138,6 +138,11 @@ Build
- Issue #14359: Only use O_CLOEXEC in _posixmodule.c if it is defined. - Issue #14359: Only use O_CLOEXEC in _posixmodule.c if it is defined.
Based on patch from Hervé Coatanhay. Based on patch from Hervé Coatanhay.
Documentation
-------------
- Issue #8799: Fix and improve the threading.Condition documentation.
What's New in Python 3.2.3 release candidate 2? What's New in Python 3.2.3 release candidate 2?
=============================================== ===============================================
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment