Specifying Conditions
00:00 In the previous lesson, I showed you how to use events to signal between threads. In this lesson, you’ll see how to take that a step further with condition objects.
00:09
Condition objects combine locking with notification. Python’s Condition
object is always associated with a Lock
or RLock
.
00:17
If you don’t pass one into the constructor explicitly, an RLock
gets created for you. In addition to the .acquire
and .release
feature of a lock, a condition adds .wait()
, which blocks until notified similar to an event, .notify()
, which allows you to control how many threads to wake up, and .notify_all()
to wake up all the threads.
00:39 Let’s revisit our tellers serving customers, but this time add in an actual lineup for the tellers. No more magical, instantaneous service for you.
00:49 I start out by creating a global condition object. Since I’m not passing it a lock, it’s creating one for me.
00:56 I’m also going to use an event to block all threads until the bank is open. This list gets used as a single line for all the tellers. As it’s a shared resource, modification of it needs to be controlled so that it’s atomic.
01:10 Let me scroll down a bit.
01:13
This time around, I’m going to have one teller, and both the tellers and the customers are on their own threads. The serve_customers()
function is for the teller, and the first thing it does is wait until the bank is open.
01:26 Then I loop while the bank is open or the queue isn’t empty. I still want to serve the customers in line after the door is closed. This context manager assures atomicity around the condition.
01:39 And inside of it, I check if the queue is empty. If so, I wait until I’m signaled. Note that waiting releases the lock and then blocks until notified. Once it has been notified, the lock gets reacquired.
01:53
To exit that last while
loop, you have to have been notified, which means there’s a customer. Here, I pop them out of the queue, then simulate some amount of time to serve them.
02:03
Once the customer’s been handled, the code goes back to the top of the first while
loop.
02:11 This function is for the customers as it also manipulates the shared queue resource, it needs atomicity. Inside the atomic block, the customer appends themselves to the queue, then notifies anyone waiting, allowing the lock to be released.
02:30 Once again, the number of threads is based on the number of customers, but this time I’ve added one more for the teller. Here I’m creating the teller’s thread.
02:39 Then, to show that the blocking event works, I sleep for a little bit, and I open the bank and notify everyone. This loop is what triggers the customers arriving.
02:50 The random amount of sleep here staggers their arrival. Then to close everything off, I call results in the opposite order that I created them, the customers first, signal that the bank is closing, and finally wait for the teller thread to finish off.
03:07 Let’s go wait in a bank lineup. Starting the program,
03:33
let’s talk about the results. The serve_customers()
routine starts out blocked, waiting on the open bank event. It stays that way until two seconds have passed.
03:42 Then the teller is ready to go. One second later, customer A arrives and is served immediately. Before the teller is done with them, customer B arrives at second number four, and C arrives at second number six. Our teller finally finishes helping customer A at second eight, and then can immediately grab customer B out of the queue. While serving B, customer D arrives at second nine. In the 10th second, the teller finishes with B and starts service for C from the queue.
04:14 Customer E arrives at second 12, and then sometime after that the bank closes its doors. At second 14, the teller finishes with C and dequeues D. Then at second 17, the teller finishes with D and dequeues E, and finally it finishes off at second number 19.
04:34 Seven seconds after the bank was closed, the teller finishes the last customer and the program is complete. I’ve heard of banking hours before, but being open for under 20 seconds seems a little extreme to me.
04:45 I guess that’s one way to convince your customers to use the ATM.
04:49 When you combine events with locks, you get conditions. When you combine semaphores with locks, you get barriers. Those are next.
Become a Member to join the conversation.