Async Techniques and Examples in Python Transcripts
Lecture: Cancelling threads with user input
0:00 Let's add one nice little trick
0:02 or nice little feature to our program here.
0:05 Notice it's doing all this work, but maybe I'm done
0:10 I've seen enough, we can get out of here.
0:12 I'd like to somehow cancel.
0:14 Well, I can somehow, Ctrl + C my way out eventually.
0:20 Well, not really right? Even that doesn't work.
0:22 Like, that's not the best.
0:24 You know, Python consists of this little like skull
0:27 cross bones thing, til we're actually finished.
0:29 That was super annoying.
0:31 Wouldn't it be nice if there was a better way?
0:33 Well, if we had set these to be daemons
0:38 at least the keyboard interrupt would've taken it out.
0:41 But still, maybe we want something a little bit nicer.
0:44 Instead of saying started
0:46 let's actually use our Colorama here.
0:50 We can come out here and say something like
0:53 press enter to cancel
0:55 And you might think, well you can just do input.
0:58 Like this, with nothing.
1:00 And technically that will block this, but how do we know.
1:06 We don't have to ask this anymore.
1:08 You can't just ask have they pressed a key
1:10 super easily in Python.
1:11 There are some ways, but it's really sketchy.
1:13 So let me show you
1:15 one other way that actually is much easier.
1:18 Instead of doing this, what I'd like to do is somehow
1:21 ask this question, hey are any of these threads done?
1:24 or rather all of the threads done?
1:27 And if they are then we're just going to exit
1:29 but if they're not, let's give people a chance to exit.
1:33 By pressing Enter in the middle
1:35 in a nice, clean way.
1:38 Okay, so how are we going to go about that?
1:40 So, this input, like I said
1:42 there are ways in Python to read whether there's input
1:46 but it's quite tricky.
1:48 We can just add one more thread and ask that question.
1:50 You know, what is the input over here?
1:52 So let's do this.
1:53 We'll create something called the abort_thread.
1:55 Going to say threading.thread.
1:58 And it's target, is going to be check_cancel.
2:05 And its daemon mode is going to be True as well.
2:09 So we need this function and we'll put it below.
2:14 It's going to be incredibly simple.
2:15 Watch this. check_cancel. Input.
2:19 Actually let's put this down there.
2:21 So here, we're going to run a thread
2:23 and that thread is just going to see if you press Enter.
2:25 If you press Enter, that thread exits.
2:28 If you don't press Enter, that thread doesn't exit.
2:30 But when we get to the end, because it's a daemon thread
2:32 it'll be canceled, alright?
2:34 Here's what we're going to do.
2:36 Going to come over here, and we're going to say while any
2:39 of these, t.is_alive().
2:42 So we can ask the thread, are you alive?
2:45 And if any of the threads come back and say
2:47 yes, we're alive, then we want to wait a little bit.
2:51 So let's wait, I don't know, five milliseconds.
2:55 Could even make it shorter, right?
2:57 Actually waiting this little bit doesn't matter, right?
2:59 So we've got three threads, we'll wait three milliseconds.
3:02 Just ask the question again Are these three threads alive?
3:05 So you decide what number goes here, but it should be
3:08 probably pretty small. And then after this we want to check
3:11 is the cancel thread alive?
3:13 We'll say if not, the abort_thread is alive.
3:17 That's it. Under Print, canceling on your request.
3:23 And that's it. We're going to stop joining the threads.
3:27 If any of them happen to still be alive
3:29 we're going to print out something
3:30 and if they are still alive after that, main thread Exits
3:33 it aborts all of them because they're daemon threads.
3:35 Okay, phew, let's see if this works.
3:39 Oh, do you know why it's not alive?
3:42 We may have missed a little bitty important step
3:46 Is it alive? No it's not alive. Why is it not alive?
3:50 It's not alive 'cause we have not started it.
3:53 Now let's try.
3:55 Alright, so it's press Enter to cancel and now it's working
3:57 If I come down here, say follow along. Boom.
4:03 Cancel entering request, done. Right away.
4:06 Won't have to hit a weird Ctrl + C, wonder what happened
4:09 We got to even print out the apps exiting.
4:11 Here's your final report. We spent 7.8 seconds.
4:15 Do it again. I hit enter real quick, canceled right away.
4:19 Of course if I just let it run, well, it'll take a moment
4:22 41 seconds or so for this to finish.
4:26 That's enough time for us to go review what we did.
4:29 It's a little bit of a hack, but if you want to be able to
4:32 ask the user
4:33 or give them an option to cancel this fork join work
4:36 well, we can create another thread
4:38 whose sole purpose is to look for user input
4:42 assuming you're not getting it from other places
4:43 at this point. And if it gets input, then it exits
4:46 you know, it can even actually capture a value
4:48 and then give that value back if you really want it to
4:51 we just don't care at this point.
4:53 So we're going to use the any operation
4:55 on any of the threads alive.
4:58 We're going to use the timeout feature, on join
5:01 and then just give a really short timeout to say
5:03 let's just check really quick
5:05 wait, give them a little time to run
5:07 and then if our question thread, do you want to exit
5:10 is still running then we're not going to cancel.
5:12 But if it is, we'll just cancel and get out.
5:15 Maybe we want to do this in a different order?
5:18 I don't know. There's a tiny bit of race condition about
5:21 potentially saying we canceled it
5:22 just at the moment that it didn't cancel
5:25 but, you know, that's threading.
5:27 It's tricky, and we're just going to leave it at this.
5:29 But, here's a way in which we can use timeout, and input
5:34 but it could've been a signal from another service
5:36 could've been our web service called and told our threads.
5:38 There's all sorts of operations
5:39 that we could use to trigger the canceling of these threads.
5:43 Cool little pattern there
5:44 to make our threaded example even better.
5:47 Oh, and let's see what our outcome was.
5:49 Look at that, it ran all the way to the end.
5:50 It got to 400 twice, like it should.
5:53 And then it printed that it was done
5:55 after about 20 seconds which is exactly what we expected.
5:58 So here is the case where we did not cancel it.