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.

Talk Python's Mastodon Michael Kennedy's Mastodon