Async Techniques and Examples in Python Transcripts
Chapter: async and await with asyncio
Lecture: Demo: Make the producer-consumer async
0:00 You've seen our producer consumer
0:01 running in synchronous mode.
0:04 We generate all the data
0:07 and then we process all the data.
0:08 And of course the first thing happens
0:10 and then the second because we're not using
0:12 any concurrent algorithms or techniques here.
0:15 Now also notice I have a produce-sync and a produce-async.
0:19 Currently these are exactly the same
0:21 so I'm going to leave you this sync program in source control
0:24 so you can start from scratch and basically
0:27 where we're starting from right now if you'd like.
0:29 But we're going to convert this what currently
0:31 is a synchronous program to the async one.
0:33 So in the end async program will be, well
0:36 the async version.
0:38 So there's a couple of things we have to do
0:40 to make our program async.
0:43 Syntactically they're simple.
0:45 Conceptually there's a lot going on here
0:47 and we've already talked a lot about that
0:49 with the event loop and slicing our functions up
0:51 into little pieces.
0:53 We've talked about the generator function
0:55 and these restartable resumable coroutines.
0:58 All of that stuff is going to come in play here.
1:00 But the API, pretty straightforward.
1:02 So what we need to do is two parts.
1:04 Let's start with the execution of these things.
1:09 So in order to run asynchronous coroutines
1:13 we can't just call them like regular functions
1:15 like right here.
1:16 We have to run them in that asyncio loop
1:19 and that asyncio loop is going to execute
1:21 on whatever thread or environment that we start it on.
1:25 And it's our job to create and run that loop.
1:29 So let's go over here and create the loop.
1:34 And it's really easy. All we do is say asyncio
1:37 and we have to import that at the top.
1:40 We say, get_event_loop().
1:42 And later we're going to say, loop.run_until_complete().
1:47 And we have to pass something in here.
1:49 So we're going to talk for just a moment
1:51 wait just a moment to see what we're going to pass there.
1:54 So we're going to create the loop
1:56 we're going to give it these items to execute
1:59 and then we're going to have it run until they are completed.
2:03 The other thing that we need to do is
2:06 we need a different data structure.
2:08 So right now we're passing this list and we're
2:11 just this standard Python list
2:13 and we're asking, hey is there something in there?
2:15 And if there is, let me get it
2:16 otherwise let me sleep.
2:17 And we technically could do that again
2:20 just with the async version.
2:23 But it turns out that asyncio has a better way
2:26 to work with this.
2:27 So we can get what's called an asyncio.Queue.
2:31 It's like a standard queue.
2:32 You put stuff in and it sort of piles up.
2:34 You ask for it out it comes out in the order it went in.
2:36 First-in, first-out style okay? The benefit is
2:40 this one allows us to wait and tell the asyncio loop
2:46 you can continue doing other work
2:48 until something comes into this queue and then wake up
2:50 resume my coroutine, and get it running.
2:53 So you'll see that it's going to be really helpful.
2:55 Now it looks like somethin's wrong. It sort of is.
2:59 Right here you see this little highlight around data
3:01 and that's because in the type annotations
3:04 we've said that this is a list
3:06 but we just need to type that as an asyncio.Queue
3:10 and then just adapt the methods here.
3:13 Okay so we don't have an append, we have a put.
3:16 We don't have a pop, we have a get.
3:20 Now we're going to see some interesting things happening here
3:23 that we're still going to have to deal with
3:25 but we're going to use this asyncio queue.
3:28 So the way it's going to work is
3:30 we're going to track the execution of these separately.
3:34 We're going to kick them off
3:36 we're going to run all of those
3:38 and then we want to go down here and say
3:40 start them and wait until they finish.
3:42 So we're going to use another concept to get them started here.
3:45 We're going to go to our loop and we'll say
3:47 create task and we're just going to pass in here.
3:51 And we actually call the data with the
3:52 call the function or the coroutine with the parameters.
3:55 But like a generator function it doesn't actually run
3:59 until you start to pull on it, make it go
4:01 and that's what create_task is going to do.
4:03 So we'll say task1 equals this
4:06 and we'll do exactly the same for the other
4:09 except we'll call it task2.
4:10 And then we want to wait
4:12 come down here and run our loop but wait until complete.
4:15 Now if you look at what this takes
4:16 it takes a single future or task.
4:20 A single thing that is going to run
4:23 and then we can wait on it.
4:24 Not two, one. And that's super annoying.
4:27 Why that's not a star args
4:29 and they do what we're about to do automatically for us.
4:31 It's okay, so we'll say final_task or full task
4:34 final_task, and we're going to go to asyncio and say
4:38 gather and what you do is you give it a list of tasks.
4:42 task1, task2, et cetera.
4:44 And then we run the final_task to completion.
4:48 Alright so this is the execution side of things.
4:51 We create the loop up here on line 10, create the loop.
4:55 We use a better data structure
4:57 we kick off a few coroutines and we convert them into
5:00 a single task that we can then run until complete.
5:04 What's interesting is nothing is going to happen
5:07 until line 21. If we printed out
5:10 hey starting this loop, starting this coroutine
5:13 starting this method, generating data.
5:15 None of that is going to actually do anything
5:17 until we start it here
5:18 we're going to block and then we'll be done
5:21 and we'll figure out how long it took.
5:23 So this is how we run coroutines
5:25 but we don't yet have coroutines down here.
5:28 So our next task is going to be
5:29 to convert those into actual asynchronous coroutines
5:33 rather than standard Python methods.