Async Techniques and Examples in Python Transcripts
Chapter: Built on asyncio
Lecture: Why do we need more libraries?
0:00 Before we get to the programming details
0:01 of these additional libraries
0:03 let's talk about why we might need them at all.
0:05 We have async and await. We have threads.
0:08 Isn't that good enough?
0:09 Well, it depends on what you are trying to do.
0:12 Yes, it's good enough, but it can be better
0:14 as we all see. So what are some of the shortcomings
0:16 of the various aspects that we've worked with so far?
0:19 Mostly around asyncio, but also around threads and processes.
0:23 One of the things that's annoying about asyncio is
0:25 you have to have an event loop in order to execute
0:29 an async function. Remember what we did is we created
0:32 a loop, and then we queued up a bunch of work in it
0:34 and where we created some coroutines
0:37 and we said run to completion or something to this effect.
0:40 And we waited on that loop. And it's not that hard
0:44 honestly, but if you have a deep architecture:
0:47 some function, calls another function, calls a library
0:50 which called another function, how do you take the loop
0:52 you have to create at the outside and pass it deep down
0:54 to the inside so async functions down there can run?
0:57 If you need to coordinate across those things
0:59 this can be really not so easy.
1:01 Alright, you end up passing stuff around or having
1:03 weird global variables that, depending on how things
1:06 are working, may actually not even work.
1:08 This is a shortcoming, and we'll see if one of the libraries
1:11 we talk about fixes it.
1:13 So this is the shortcoming, and each of the libraries
1:15 we're going to talk about has a certain way to address this.
1:18 Asyncio.future is not thread-safe.
1:20 Now, that may sound weird to you, but remember
1:22 asyncio doesn't actually use other threads.
1:25 It's like an event loop on a single thread
1:27 so it's not thread-safe.
1:28 But if you want to mix it with threads, that would be better
1:32 if it were, right?
1:33 On the converse side, we have concurrent.future.
1:36 Remember this comes back from the ThreadPoolExecutor
1:38 or ProcessPoolExecutor?
1:40 When we queue up work, that thing cannot be used with
1:43 async and await. It cannot be directly awaited
1:46 which is annoying. Wouldn't it be great if I could
1:48 get one of those back and mix it in and await on it?
1:52 Well, you can't. Future.result is a blocking operation.
1:57 It's generally good. If it's not done
1:58 you want to get the work back.
2:00 However, if you're doing this in an event loop
2:02 you could actually clog up the event loop
2:05 and if somehow there's a loop
2:07 you somehow create the future
2:09 which is running the event loop
2:10 and then you call a block on it on another slice of
2:13 those tasks running there
2:15 you will deadlock your event loop.
2:16 Not great. On the other hand
2:18 where future.result is a blocking operation
2:21 asyncio.future.result will actually throw an exception
2:25 if you haven't completely waited for it to be done.
2:28 So, depending on which type future you have
2:31 it doesn't behave the same, that's also hard
2:33 so we'll see of unifying stuff happening here.
2:36 Async functions, as in async def - that's a function name
2:39 always execute in asyncio loop. They don't run in threads.
2:44 They won't run in multiprocessing mode.
2:47 None of those types of things.
2:49 However, may we have some work, and some of it is based on
2:52 asyncio, but other parts of that work that we're trying
2:55 to do altogether might be computational.
2:58 Or maybe it's working with something that talks to a
3:00 database or network, but that library doesn't support
3:03 asyncio directly.
3:05 It would be nice if we didn't have to completely
3:07 have different API's and ways of working with them
3:10 if we could unify that. Well, cancellation and timeout
3:14 are tricky things than threads and processes.
3:17 Thread local storage, which we have not talked about
3:19 does not work for asyncio concurrency.
3:22 So thread local storage is kind of like a global variable
3:25 but each thread has its own copy of that data.
3:30 Each thread has its own values for that global data.
3:34 Imagine something like Flask.
3:37 Flask has a global request object, and each time a
3:40 request comes in, you can just access it out of thin air
3:44 in your view method. And it has the value of that particular
3:47 request. And that's fine if it's single threaded.
3:50 You can just set that value, do the function call
3:53 and then unset it.
3:55 But remember, asyncio has only one thread for all these
3:59 interwoven operations. So thread local storage no longer
4:02 means what it used to mean.
4:04 Well see, none of the libraries here directly address this
4:07 but further down the line when we get to the async web stuff
4:11 that's one of the problems that's going to have to be addressed.
4:14 Testing concurrent code can also be tricky.
4:17 These are some of the shortcomings that the libraries
4:20 we were talking about will help us address
4:22 and they also bring their own cleaner programming API
4:25 for certain things that they're built for:
4:27 coordination, parent/child tasks, things like that.