Async Techniques and Examples in Python Transcripts
Chapter: Thread safety
Lecture: Demo: Breaking a deadlock
Login or
purchase this course
to watch this video and the rest of the course contents.
0:00
So here you see, we have our various print statements and we found that we go taking first lock taking first lock, done.
0:07
This is deadlocking. How do we fix it? Well, the fundamental problem here is that different threads could say take first a lock on account A then B
0:16
or maybe they go B then A and it's that order that is the problem. If we could guarantee all threads first took a lock on A
0:23
and then they took a lock on account B regardless of whether that was the from or to account well then we could actually be great.
0:30
We could just take those locks and we wouldn't have this deadlock problem. So, how do we fix that?
0:36
Well, we can just decide some order for the accounts. If accounts had ID's we could figure out which one is lower and use that one first.
0:44
Our account just has a balance and a lock but we can just ask for the memory address basically the Python ID of the pointer and that'll be good enough.
0:53
So we can just say lock1, lock2. This is going to be a little clumsy and I can't think of a great way
0:59
in Python to write it better, so let's just do this. We'll say from_account.lock to_account.lock, and we're going to do a test.
1:10
If the id can get the id basically gives you back a number based on the pointer from_account is less than id of to_account.
1:18
We'll say else, and return these in a different order. Then we just take this and say, you know what? We're using lock1 and we're going to use lock2.
1:32
Now let's try it and see if it deadlocks. The deadlocked one is still running again. Kill it off there.
1:42
It's slow because it has all these print statements. But it's not deadlocked, is it? Boom, it finished. Let's get rid of these.
1:52
Alright, now we can run it without any of those print statements, just let it run pure. Whoo! So, do you see the huge performance benefit
2:01
we got from this? We went and we said, look, we have six threads I have 12 cores, we have a bunch of different accounts.
2:07
Let's actually let all of this happen in parallel and we're going to do way better. Our program is going to be so much faster
2:14
because now, as long as it's not involving any of the two same accounts we can actually do concurrent transfers.
2:22
Right, now we don't have that many here. If we added more accounts, you might think this gets better. But it turns out, this part gets faster
2:32
but this part down here, those parts actually get equally slower. So, adding more accounts, which would add more concurrency actually just still
2:43
kind of makes it more computational. And it turns out that it's just worse to remember what did we get before. Let me pin that, run that safe bank.
2:54
This is the global one. We got 1.1 seconds. This new fancy high-performance version gave us 1.3 seconds and a bunch of deadlocks.
3:04
So, I'm not saying you should never use fine-grained locks, I'm just saying you should think about whether it makes sense
3:10
to use fine-grained locks or coarse-grained locks. And really the most important takeaway from this whole section has to do with this.
3:20
If you're taking two locks from two different things you have to make sure you're always taking them in the same order, or there's a good chance
3:26
you're going to deadlock your application. You don't want to do that, trust me. That is a really, really bad place to be.
3:32
It's no fun to figure out what happened or why this is happening. Taking two locks, always take them the same order
3:38
you don't have to use my little ordering technique figure out some way to order it but this works for this particular situation.