Python for the .NET developer Transcripts
Chapter: async and await in Python
Lecture: More threading with unsync
0:00 Remember this picture?
0:01 I said you can use asyncio
0:02 to weight on I/O type of completion.
0:05 You can use threads if somehow those threads are being
0:08 the GIL is somehow being released like
0:09 with the thread.sleep
0:11 or we're talking about some kind of network thing.
0:13 But if you wanted true computational and parallelism
0:15 you got to use multi processing
0:17 and whoo! You're like, jeez why is this so complicated?
0:19 Why can't I just use a task for all of them?
0:21 That's where unsync comes in!
0:23 So, we saw unsync already simplified the
0:25 asyncio API. What it also does is unify asyncio threads
0:30 and multi processing all behind async in a way.
0:33 And it's actually super super cool.
0:35 So let's go look at that.
0:37 Now, there's not really any parallelism
0:40 that's super interesting here
0:41 but let's just go and throw in some stuff
0:44 that we can do. So, here's the rules about unsync
0:47 that I haven't talked about yet, because it
0:49 actually does more than just asyncio.
0:51 If I apply this decorator to an asynchronous function
0:55 it will run on this implicit, behind-the-scenes
0:58 AIO event loop, that it manages on a background thread.
1:02 Okay? That's pretty awesome!
1:04 So, we just put it on asynchronous methods
1:06 and it does all the loop juggling for us.
1:08 But it does more. If I did this and I put it here
1:12 this will let's go like this, maybe make a little comment.
1:16 So this will run this function on a background thread.
1:22 Yes, so if we say unsync
1:23 on a non-asynchronous method, it means it cannot
1:27 run on an event loop.
1:28 Unsync will go well, you still want to do something
1:30 asynchronous with it, so let's run it on
1:32 a background thread. And see this get title from html?
1:34 Now, here now we have to await this as well.
1:38 As if it's going to complain because the style is
1:42 let's just not worry about it.
1:43 It's still going to work
1:44 like I said, there's some weird type detection stuff
1:46 between the decorators and
1:49 PyCharm, let's just see it works.
1:53 Yep! Oof, took forever that time!
1:55 Let's try it again.
1:56 You know, that's probably a lot slower, isn't it?
1:57 Oh, no no, it's probably just timing.
1:59 Look, it's much faster.
2:01 It does seem consistently a little bit slower.
2:04 Ah, I don't know. Networks!
2:06 If we put it like this, it's going to run
2:08 on a separate thread, and then it becomes
2:11 something we can await
2:12 exactly like if it were asyncio.
2:14 Right, that's super cool already.
2:16 But remember, this the way this works
2:19 it doesn't make any sense to run this on a background thread
2:22 because the GIL is still at play.
2:24 Even if unsync is trying to manage the API for us.
2:27 It's still not really going to run more
2:29 than one instruction at a time.
2:30 So it doesn't help us.
2:32 What we have to do in Python is run
2:34 in a sub process.
2:36 Now it's pretty easy, basically go to this multi processing API
2:39 and you give it a function
2:40 you say run that in another process
2:41 and give me the return value.
2:42 Though it's not, like, a lot of work that you have to do
2:45 but it is a very different mode of execution.
2:48 And that's really, at the moment, what's required
2:50 to do computational parallels in Python.
2:52 Well, unsync has a cool thought on that as well.
2:55 Unsync says, you know, we can go over here and say
2:58 @unsync(cpu_bound=True). And if we say that
3:04 then unsync knows, hey this is a computational thing
3:07 Python doesn't do that well with threads.
3:09 So let's do that with multi processing.
3:11 Just changing that to that
3:15 converts from using threads to using multiprocessing.
3:18 And I guess I can put a little comment here as well.
3:21 Here we go. So, this is the more appropriate one.
3:24 It might be slower, there is the start up
3:26 of the processing and this execution is really fast.
3:29 So, let's run and see if it's any better.
3:30 I might just comment it out and say we're not going to do this.
3:33 But, it's incredible that when these approaches are relevant
3:37 for the type of execution, the algorithm you have
3:39 that all you have to do is put an unsync decorator
3:42 on a regular function, and it becomes something that
3:45 runs on a thread and you can await it
3:46 or CPU bound equals true
3:48 and it's something that runs in another process
3:50 and you still can await it down here like this.
3:53 Nevermind that warning
3:54 that's Pycharm badly guessing at what it should do.
3:58 Alright, again done. 1.10 seconds. Try again.
4:03 1.12. It's not really making it better. 1.09
4:06 It's not making it much worse.
4:07 I think this is mostly just the network discrepancies
4:09 that we're seeing.
4:11 Right, well, I guess I'll leave it like this
4:14 like if this were me and these numbers were coming out
4:15 that similar I would just
4:17 just call this the regular function.
4:19 I wouldn't go to that trouble.
4:20 But, there are many many examples
4:22 where this execution takes a lot longer.
4:24 And this would make a big difference.
4:25 Like, lets go down here and say 'time'
4:28 import 'time' at the top.
4:30 Yeah, we can trick it here.
4:32 Sleep, this is like, this is like the sleep
4:34 thread dot sleep and dot net.
4:36 Let's say it takes an extra hundred
4:38 oh, that's seconds
4:39 Let's say it takes a hundred milliseconds
4:42 or two hundred milliseconds to do this.
4:44 And first, let run it directly. Down here
4:48 You're not going to await it.
4:49 That's pretty, pretty tall isn't it?
4:51 Can't await it if we're going to do that.
4:54 So let's just see how long it takes like that.
5:01 There you can see we got the
5:02 the data back right away
5:03 and it started processing it.
5:05 But it could only process them one at a time.
5:07 So that was two hundred milliseconds per time.
5:09 Right, as that number gets better, or bigger
5:11 it gets worse.
5:12 So let's go over here and say we're going to try to use
5:14 multiprocessing to do that.
5:15 Does it make it better? Uh, it's actually
5:23 I just have to await down here, remember?
5:26 I didn't get I got a coroutine chain back
5:28 and then I tried to make it green.
5:30 Apparently you can't make coroutines green.
5:31 So weird! Any better? Ah, a little bit!
5:38 Three, Four, yeah I'm not sure it's any better.
5:45 I don't know how much parallels I'm
5:46 we're getting out of our multiprocessing there.
5:50 Anyway, I'm going to comment these out
5:51 and put it back just run it normal.
5:54 But, this unsync has a really cool API.
5:56 Oh yeah, let's take away this as well.
6:00 Put it back. And it goes beyond just working with asyncio.
6:05 It allows us to take regular
6:06 non asynchronous methods
6:08 and convert them to either threaded mode
6:10 or multiprocessing mode.
6:12 Which is pretty slick.
6:13 If you come from a C# background
6:15 you definitely should check out unsync.
6:17 It really is basically an adaptation of Python's model
6:21 over to what you would get from
6:22 the task parallel library and C#.