.. _programming: Multi-threaded Programming =========================================== Modern programming languages do vary slightly in how threads of a process are created, managed and synchronize access to :term:`critical section`\ s. But C/C++, Java and Python all provide the needed facilities. We will mostly look at Python's facilities, which are quite straight forward. Python provides synchronization facilities very similar to C/C++, except at the object level. Python's synchronization objects take care of most of the lower level details for us. Yet, it still provides a great deal of flexibility to the programmer. For more details, see `Python's documentation `_ .. index:: thread creation Creating threads in Python --------------------------- Threads are created via the :class:`threading.Thread` class from the :mod:`threading` module. There are three ways that this can be accomplished. * Create Thread instance, passing in a function * Create Thread instance, passing in a class * Subclass Thread and create subclass instance .. module:: threading :synopsis: Tools for creating and managing child threads of execution .. class:: Thread(target, args) :param target: identifies the code (function) for the new thread to execute :type target: callable function :param args: is the arguments to pass to the target function :type args: list .. method:: setDaemon(n) Manage the persistence of the child thread relative to the parent. *n of True or 1* means that the child thread dies if the parent dies first. *n of False or 0* means that the child thread can keep running after parent is finished. .. method:: start() Begin execution of the thread now. .. method:: join() The current (parent) thread should suspend until the child thread terminates. In the following simple example, ``threadcode`` is a function with arguments of *arg1* and *arg2*:: import threading # . . . t = threading.Thread( target = threadcode, args = [arg1, arg2] ) t.setDaemon(1) t.start() # . . . t.join() .. _sync-tools: Synchronization tools (some of them) ------------------------------------- .. _py_Lock: Python's Simple Lock ^^^^^^^^^^^^^^^^^^^^^ .. class:: Lock() A simple :term:`mutual exclusion` lock used to limit access to one thread. This is a :term:`semaphore` with s = 1. .. method:: acquire() Obtain a lock. The process is blocked until the lock is available. .. method:: release() Release the lock and if another thread is waiting for the lock, wake up that thread. Here is how to provide :term:`mutual exclusion` access to a critical section:: import threading L = threading.Lock() L.acquire() # The critical section ... L.release() .. _py_semaphore: Python's Semaphore ^^^^^^^^^^^^^^^^^^^ .. class:: Semaphore(s) A general purpose lock used to limit access to s threads (:class:`Lock` and :class:`Semaphore` are almost the same) -- the default value of s is 1. (see :ref:`semaphore`) .. method:: acquire() Obtain a :term:`semaphore`. The process is blocked until the semaphore is available. .. method:: release() Release the semaphore and if another thread is waiting for the semaphore, wake up that thread. The following code allows up to five threads in the :term:`critical section` at one time:: import threading S = threading.Semaphore(5) S.acquire() # # The critical section ... # S.release() .. _py_Condition: Python's Conditional Monitor ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. class:: Condition() Implementation of a :term:`monitor`. (See :ref:`monitor`) It allows a thread to wait and be signaled by another thread based on some condition possibly becoming *True*. .. method:: acquire() Obtain the monitor's internal lock. The process is blocked until the lock is available. .. method:: release() Release the monitor's internal lock and if another thread is waiting for the lock, wake up that thread. .. method:: wait() Release the underlying lock, and then block until it is awakened by a :meth:`notify` or :meth:`notifyAll` call for the same condition variable in another thread. Once awakened, it re-acquires the lock and returns. .. method:: notify() Wake up one of the threads waiting for the condition variable, if any are waiting. .. method:: notifyAll() Wake up **all** of the threads waiting for the condition variable, if any are waiting. The :class:`Condition` class provides a level of abstraction, which can greatly simplify the solution to many problems. Notice that the conditional :func:`wait` statement should always be used inside a `while` loop. This ensures that the logical condition is still *true* before the thread enters the :term:`critical section`. It could be that another thread saw a condition, which prompted it to issue a :meth:`notify` statement, but by the time our thread returned from :meth:`wait`, the condition is no longer true. The evaluation of the condition must be done while holding a mutual exclusion lock. It should be pointed out that the Python :class:`Condition` class contains a mutual exclusion lock, which may be manually acquired or released. The :meth:`wait` method releases the lock, and then blocks until it is awakened by a :meth:`notify` or :meth:`notifyAll` call for the same condition variable in another thread. Once awakened, it re-acquires the lock and returns. :class:`Condition` is especially useful for problems such as the producer -- consumer (:ref:`bounded_buffer`) problem, where each thread may only proceed if certain resources are available. The example below uses a global boolean variable to coordinate access to the :term:`critical section`, but a boolean function or class method could also be used. :: import threading global available C = threading.Condition() C.acquire() while not available: C.wait() available = False C.release() # The critical section. Note that no locks are held. C.acquire() available = True C.notify() C.release() # alternately, we could notify all waiting threads # C.notifyAll()