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