Reputation: 1063
Trying to wrap my wits around how threading
works. The high-level language in the docs and source code is helpful up to a degree but still leaves me scratching my head. What exactly, in terms of data structures, is the relationship between Thread
and Condition
objects? What does it mean when a thread "releases" a lock? That the Condition
object dequeues its reference to the thread? Is there a lower-level description of these interactions, preferably in Python terms, to be found on the Internet?
Upvotes: 1
Views: 171
Reputation: 104762
A Condition
maintains a list (actually a collections.deque
) of what are notionally threads, waiting on the condition. It actually stores locks that the waiting threads are blocked on, but thinking of it storing the threads is a conceptual shortcut if you don't care too much about the implementation. The list is initially empty, but any time a thread calls the Condition
's wait
method, it will create a new lock and add it to the list before blocking on the lock (conceptually, this adds the thread to the list, and suspends it). Locks are removed from the list after another thread calls notify
or notify_all
, which unlocks one or more of the lock objects in the list, waking up the corresponding threads.
Releasing a lock means unlocking it. It's a basic operation on a Lock
object (the reverse of acquire
, which locks the Lock
). A lock is "held" in between an acquire
and a release
, and only one thread can hold a Lock
at a given time (other threads will either block in acquire
, or the operation will fail, perhaps after a timeout). You can use the context manager protocol to call acquire
and release
for you in simple cases:
with some_lock: # this acquires some_lock, blocking until it's available
do_stuff() # some_lock is held while this runs
# some_lock will be released automatically when the with block ends
Each Condition
object is associated with a Lock
, either a pre-existing one that you pass to its constructor, or one it creates internally for you (if you don't pass anything). The main Condition
operations (wait
and notify
, and their variants) require that you already hold the associated lock before you call them. You can do the lock operations directly on the Condition
object itself, since it proxies the Lock
's acquire
and release
methods (and the equivalent context manager methods).
The Condition
class is written in pure Python, so if you want to know how it works on a low level, there's probably no better source of information than the source code itself!
It might also be useful to see how a Condition
is used to synchronize multithreaded access to an object. A good example of that is the queue
module in the standard library, where each Queue
uses three Condition
s (not_full
, not_empty
and all_tasks_done
) to efficiently manage threads that are trying to access or modify its data.
Upvotes: 1