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
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.