There’s more to working with multithreading than locking code and avoiding deadlocks. You also need to know how to synchronize activities. It’s not so hard once you understand.
Events are objects that you can create and wait on from one thread while signaling the event from another thread. When a thread is waiting for one or more events, it gets suspended so it doesn’t continue to consume processor time or resources. And when a thread wakes up from waiting, then it knows that the event it was waiting for has happened. That is assuming a wait timeout didn’t happen first.
Specifying a timeout value is a good idea when you have other work your thread could be doing or you want to just give up and try something different.
For the thread that signals an event, it can usually choose to signal the event for all waiting threads or just a single thread.
And the last topic that this episode explains is how you can wait on objects other than events. Listen to the full episode or read the full transcript below.
It might sound scary when I say you need to synchronize thread activities. To me this brings to mind images of secret agents setting their watches to the same second and then each one later counting down to some predetermined time before pushing a button or opening a door.
While I suppose you could try to write your code like this, it would be clunky and fragile at best. There’s a much better way. And the cool thing is that it comes natural to us and we do things like this all the time in real life.
Imagine you’re at work and about to go home. The courteous thing to do is to call home first to let your family know that you’re on your way.
Or you want to stay a bit later than planned and somebody is supposed to arrive to pick you up. What do you do? You call and let the other person know you want to stay longer and that you’ll call again when you’re ready.
These are notifications. You can also think of them as events. Because a notification is just one kind of event.
Let’s say you want to wake up at a certain time. What do you do? You set an alarm. If you’re baking a cake, you set a timer. An alarm clock and a timer are just triggers that set off an event. The event is the ringing of the alarm.
Imagine a group of runners about to begin a race. Do they each start running whenever they feel ready? No. They all wait for a single event of the firing of the starting gun.
What about when you go see the doctor? You first wait in the reception area with all the other patients. You know, I always wondered if we’re called patients because of all the waiting we have to do. This is different than the runners though. This time, the event is when a nurse comes out and calls somebody’s name. It’s your turn only when your name is called.
And let’s not forget the whole traffic light system. When the light turns green, all the waiting cars start moving.
I could keep going. As you can see, events come natural to us and we use them all the time without even realizing how much our lives depend on synchronization. Computer threads are just the same.
I’ll explain more right after this message from our sponsor.
( Message from Sponsor )
Based on all the examples I gave about events, you can see that there are quite a few different types. Some events such as the traffic lights apply to anybody who wants to go through an intersection. They’re public.
Other events such as the phone call you make when you’re about to come home are private.
Some events are a one time thing such as when your cake is done baking. Sure, you can always bake another cake, but that’s a different cake. Other events can be reused and cycle through states such as the traffic lights.
Some events let everybody through such as the starting gun and the traffic green light.
Other events let just a single person through at a time such as the doctor’s office.
There’s just one more concept that I want to bring into this discussion before tying everything back to multithreading. What do you do if you suddenly realize that your cake has been in the oven a suspiciously long time and still no timer has went off? You could just keep waiting and trust that you’ll be notified eventually. Or you could give up and go check the cake anyway.
That’s it really. All I need to do now is explain how all this maps to multithreading. But I hope you realize that this isn’t a topic to be afraid of. If you understand the concepts I just explained, then you already have the basics of thread synchronization.
I’m keeping this episode a bit abstract because each language that supports multithreading tends to have its own way of doing things and its own set of names to call everything. Most languages eventually converge on locks and events.
When you have a thread that you want to synchronize its activities with that of another thread, then you can’t reliably use the system time because you don’t know how much time each thread was actually given to run. What you do is create an event and in one of your threads, you wait on this event. Waiting on an event is very efficient and puts the thread to sleep for a while.
Then on another thread, you signal the event. This might be called notify and usually there will be some way to either notify all threads waiting on the event or notify just one thread. Unfortunately, you can’t specify which thread to notify. At least not in any language or operating system that I know about. That would be interesting if it exists. What normally happens when you notify a single thread is that the operating system will select a deserving thread based on a system that only it understands.
If a thread is woken up because the event it was waiting on has been signaled, then it may need to reacquire its locks. The main thing is that the thread knows for certain that the event it was waiting on has happened.
You don’t have to wait on just events. There are other things you can wait on and the meaning of waiting will change depending on the type. For example, you can wait on another thread itself. What does that do? How do you signal a thread? It just means that you wait until the thread ends.
You can wait on another process. This is just like waiting on a thread and the process will be signaled when it ends.
Or maybe you need to wait on the user to type some keys on the keyboard. Or you need to wait on some data to be received. All of these are forms of synchronization.
So the next time you need to coordinate your code with something else, remember that you can wait on various types of objects including your own events that you can create and control. When you wait, you can even choose to wait on multiple things at the same time and optionally give a timeout to stop waiting in case none of the things has signaled that it’s ready.