Async Techniques and Examples in Python Transcripts
Chapter: Thread safety
Lecture: Demo: An unsafe bank
0:00 Welcome to our next set of demos.
0:01 So we're going to have two or three different sets
0:04 of programs we're going to go through.
0:06 We have an unsafe bank
0:08 which you should never, ever put your money
0:10 in the unsafe bank. It may lose your money.
0:12 Trust me this is bad.
0:13 And we have this thing I've called a safe bank.
0:16 Now the safe bank starts out unsafe.
0:18 Our job is to take the unsafe bank and make it safe.
0:22 How do we know it's unsafe?
0:23 Well let's go and see what it does and we'll talk about it.
0:26 So down here we have our main method.
0:28 Well, first of all we have different accounts.
0:30 They're extremely simple.
0:31 They don't have to keep track of much.
0:33 So we just have a balance right?
0:35 In reality they have owners and numbers
0:37 and things like that but ours just has a balance.
0:41 And so we're going to create a bunch of accounts
0:44 and we're going to figure out what the total number is
0:46 in these accounts.
0:47 The total value of these accounts and then
0:49 we're going to go about transferring money between them.
0:52 One of the mathematical properties that should always
0:55 be true if we're taking money from one account
0:58 and putting it into the other all within the same bank
1:01 no matter how many accounts are there, the number
1:03 the overall total should always be the same.
1:06 Now that's excluding real things like fees
1:08 and external banks and stuff like that but
1:11 this is a simple bank.
1:12 What we should be able to see is at no time
1:16 will we ever not have a total amount
1:18 whatever that total turns out to be
1:20 of money in the overall bank
1:22 if we look at all the accounts, yeah?
1:24 And that's what validate_bank() does here.
1:28 So we'll go look at that. Now we're going to kick off what is that, five threads?
1:31 Going to kick off a bunch of threads
1:32 and they're all going to run this do_bank_stuff()
1:34 they're all going to have the accounts.
1:37 I'm going to pass the total so they can do a little check
1:39 to see if the bank is still valid.
1:42 We're going to kick them all off
1:44 and then we're going to wait for them to finish.
1:45 That's standard stuff.
1:46 We talked about that before.
1:48 Now down here in do_bank_stuff()
1:50 we're just going to go for 10,000 times around.
1:54 We're going to randomly get two different accounts.
1:58 We're going to create a random amount between one and $100.
2:01 We're going to transfer money between them
2:03 and we're going to validate that it's fine.
2:06 So let's look at that transfer
2:07 that's probably the last thing to do.
2:09 So to transfer, super easy.
2:12 We're given a from account to take the money from
2:15 a to account to put the money in and how much to do.
2:19 Add a little error checking in the top
2:20 and then we just go take the money out of the top one
2:23 and put it into the to account.
2:26 Take it out of the from, put it into the to.
2:27 And then just to simulate some real activity right
2:31 something other than Python's GIL
2:34 not really lettin' us do anything
2:35 we're going to say, let's just give up our current time slice.
2:39 We're not even sleeping for one millisecond
2:41 but just imagine that this operation
2:45 that we're trying to do here, this do transfer
2:47 takes one millisecond right?
2:49 It's just a little, tiny bit of time
2:51 that is going to be the key to all sorts of problems.
2:54 It just means these problems are going to be more observable
2:58 but it doesn't actually mean the problem would go away
3:00 if I were to comment that line out.
3:02 Okay finally, final thing
3:03 is we're going to say validate_bank().
3:05 That is just to sum up the current balance and see
3:08 if the current is the total and things like that.
3:11 So if the current and the total ever differ
3:14 we've got something wrong right?
3:16 That's a property of our bank
3:17 we should always have the same money
3:19 if we're just shifting around accounts.
3:21 Whew, well that's the introduction.
3:23 You want to run it and see what happens?
3:24 Let's kick off these five threads that are each going
3:26 to do 10,000 requests across balance transfers
3:31 between these various accounts and we'll see how it works.
3:35 We're going to run safe bank.
3:37 Alright so let's kick that off.
3:39 Huh, what do you think?
3:41 Well first of all, let's imagine we didn't do that.
3:43 Let's put this away for one second.
3:44 Let's imagine it's single-threaded.
3:47 How does it work? We're starting transfers
3:49 everything was good in the beginning
3:51 did a bunch of transfers, was good in the end.
3:54 That's how it should work all of the time
3:57 but this is not thread-safe and I have set it up
4:00 to be pretty likely to hit that error actually.
4:03 If we run it now though
4:04 you can see what's happening.
4:08 One of these, let's say this thread
4:10 is comin' along and transferring money
4:12 from account A to account B.
4:13 It's halfway through so it's taken money out of A
4:15 but not in B.
4:17 This one just finished a transfer and calls validate.
4:19 It sees the invalid state that the first thread
4:22 on line 21 has created, and it shoots out an error
4:25 oh my gosh warning warning warning.
4:27 Eventually the top thread here puts it back
4:31 into a valid state but this is not good okay?
4:35 So here you can see tons and tons of errors in this bank
4:39 like look at that scroll bar.
4:40 That is bad news bears right there.
4:42 But at the end everything is fine, no problem.
4:44 It's just a matter of
4:45 did you observe that invalid state or not?
4:48 And it turns out because so much stuff is happening
4:50 you're observing it a whole lot.
4:53 Would you like to put your money in this bank?
4:55 It's called safe bank but it is nowhere near safe, yet.
4:59 So our goal will be to use the various constructs
5:02 in Python to make it safe.