Understand the following example, in fact, with the java wait/nofity is the same as the same thing
import threading # Conditional variables for complex inter-thread synchronization locking """ Demand. Man: Miss, how are you! Woman: Hmph, you want to pick me up? Man: Yes, I want to pick you up. Woman: Get lost, no way! Man: Che, you're so ugly, and you're so hung... Woman: None of your business! """ class Boy(): def __init__(self, name, condition): super().__init__(name=name) = condition def run(self): with : print("{}:older sister (also polite term of address for a girl or woman slightly older than the speaker),How are you?!".format()) () () print("{}:That's right.,I want to hit on you.".format()) () () print("{}:correspond to,You're so ugly., You're still hanging in there....".format()) () () class Girl(): def __init__(self, name, condition): super().__init__(name=name) = condition def run(self): with : print("{}:croon,You want to hit on me??".format()) () () print("{}:beat it!,Not even the door.!".format()) () () print("{}:None of your business.!".format()) () () if __name__ == '__main__': condition = () boy_thread = Boy('Male', condition) girl_thread = Girl('Female', condition) boy_thread.start() girl_thread.start()
Condition's underlying implementation of the __enter__ and __exit__ protocols. So you can use the with context manager
As you can see from the __init__ method of Condition, it also maintains an RLock lock at the bottom.
def __enter__(self): return self._lock.__enter__()
def __exit__(self, *args): return self._lock.__exit__(*args)
def __exit__(self, t, v, tb): ()
def release(self): """Release a lock, decrementing the recursion level. If after the decrement it is zero, reset the lock to unlocked (not owned by any thread), and if any other threads are blocked waiting for the lock to become unlocked, allow exactly one of them to proceed. If after the decrement the recursion level is still nonzero, the lock remains locked and owned by the calling thread. Only call this method when the calling thread owns the lock. A RuntimeError is raised if this method is called when the lock is unlocked. There is no return value. """ if self._owner != get_ident(): raise RuntimeError("cannot release un-acquired lock") self._count = count = self._count - 1 if not count: self._owner = None self._block.release()
As for how wait/notify works, it's still a bit confusing .....
The wait() method source code has three lines of code like this
waiter = _allocate_lock() #Get a lock from the bottom, not a Lock lock.
()
self._waiters.append(waiter) # then add this lock to _waiters(deque)
saved_state = self._release_save() # Is this releasing that lock from __enter__?????
notify() method source code
all_waiters = self._waiters waiters_to_notify = _deque(_islice(all_waiters, n))# n from _waiters if not waiters_to_notify: # If None, end return for waiter in waiters_to_notify: # Cyclic release () try: all_waiters.remove(waiter) #Remove_from_waiters except ValueError: pass
The general idea is: wait creates a lock from the bottom, acquires it, puts it in a deque, and then releases the with lock, notify takes the lock out of the deque and releases it.
This is the whole content of this article.