hey it's chuck here have you wondered about the async and await keywords and what's really going
on with those in c sharp well i've had several people ask me about it so i put together a very
simple demo that explains what's going on behind the scenes with async and a weight
so let's start with a simple example this is a sequential example but the pieces are there so
that we can demonstrate what happens when we make the changes okay so mechanic has three methods
wash car get gas and change oil so we're going to sleep in each one of these methods for some
random amount of time the get gas method returns that random number so now from main we're going to
call those methods and we'll indicate when we're finished with everything so when i run this you
can see that it it kind of runs slow it's going to take a little bit for this to finish up and
the output statements that we have embedded in this code are are showing what the
thread id is so this one that you're seeing is the thread id so this is single threaded
so she's gonna run through everything stays in thread one now let's make a minor change
to the mechanic class let's make a change to it so that it returns a promise to finish its
work in the future sometimes so we're going to take that sleep and put that inside of a task
by calling task dot run and inside that code we're going to do that sleeping the work involved now
to make this a little bit more realistic with what happens with a sink and await
there were statements that happened after the work in particular the statement that said that
we were finished that was after the task that was created so in order to be closer to what's
going to happen with async and away i've got to continue with so we're going to create a task
that's going to run effectively the sleep where the actual work of washing the car is being done
but then when that's finished a callback or an event will create another task to continue uh
the remainder of the method so the rest of this method has to execute whenever the part that we
were waiting to finish can finish because we can't return we can't say that the the car wash is done
until it's actually done so we're going to use continue with to indicate that now
i could have also put a just a dot weight here we could have just done dot weight and then uh
let's see that's a method so put the parentheses on it and then
instead of continue with we would just have the regular console.writeline and of course we don't
we don't return up there any longer so we could have done it that way now i can't return a task
from this but i no longer need to now i can just return void because um because the the method is
completely finished when it returns it so so it's internalized now that doesn't help us much because
we've got the weight appearing here immediately so there's no parallelism there's this is
still going to execute sequentially okay so i'm going to put it back like it was
now the weight would be okay if we had some work we could do in between so if we could
start the task run it let some stuff happen do some other tasks as well or some other code
then later wait for those to complete that would that would have served a purpose that would have
had meaning each one of these methods we've converted them over so that they return a task
and that task is a combination of the original work plus the code that came after that
and the code that comes after that is executed as part of the continue with
in the get gas method because it returns an integer it now returns a task with an
integer type result so let's take a look at main real quick we're going to call each one of our
our work items to get gas wash car and change oil and in each one of these the result is no longer
a value but instead a task so now i need to wait on those tasks to complete
now i could just call weight on each one of those in sequence if i really don't care
what order then i could just wait on each one of them one by one or we can take advantage of a a
method that's in the task library called when any or when all we could use that one as well
i'm going to do when any so that i can pop out of this each one of the tasks as they complete
so we've created a list it's got our three tasks in it we've started the tasks
and i'm indicating what thread we're running in what thread we're executing in and now
we're going to pop the tasks off one at a time getting the result of that task as it completes
and taking that task out of the list so that we can come back and wait for the next one so we
run this one you know we see the the output come much much more quickly but one thing to notice
is that the thread number one which is our main thread thread number one is uh responsible for
the first part of each method so when we get inside the change oil wash car and get gas
methods we're still executing as thread number one the tasks that are started that task dot run
we're executing is task number six seven and eight and these are arbitrary they they'd be
very different uh depending on on your execution now you'll notice that get gas done which is the
continue with that was a technically a different task it got assigned to get a different thread
so the get gas task executed as thread number six and then when that continue with executed it was
thread number 10. now when you run this you're going to get different results because we have
random numbers and so the timing of the sleeps and everything will change everything up uh change oil
executed as task number eight and uh wash car same thing so that's what i normally see when i when
i run this uh the times are not that different so they uh you know it's a fairly short time so they
oftentimes they just get the same thread now if you created a lot more work and a lot more tasks
you'd see a lot more things happen like we do with the oil change where the oil change continue
thread id is actually different uh or actually i should say the get gas where its threat id for the
original task was different from the continue with in any case when we are finished with
main or when each task is completing in main you'll notice that main is still executing with
its original thread thread number one and then main eventually gets finished in in exits okay now
this is because we did the continue with and we recognize the fact that there's there's two
different things that happen there's the time consuming operation which was our thread.run or
task.run operations okay so if i go back again to our original so this task dot run this is where
we're actually doing the work this is the time consuming part and then we had another task that's
created with the continue with that that executes when the first one is finished obviously because
we're announcing that we're finished we can't put that uh immediately after so this is how the async
and await actually kind of works as well when we encounter and await the remainder of the method
has to be the basically the same thing as a continue with sort of sort of because you're
going to see some differences here okay but but this is kind of like what you might do
if you were programming without async and await if you were doing this before those keywords were
available it's not quite there we're not quite exactly the same thing but it's it's close okay
now let's look at the async and await version and it because this does so much for you uh
so first of all let's take a simple one we're going to look at the wash car task
so it is still returning a task just like we did with the previous variant but now i've annotated
as the method as an asynchronous method now this does a couple of things so one i need to return
my return statement which is implied here just needs to return the type that i'm supposed to be
returning so task means return void in the second case in the get gas method remember it returned an
integer so i'm still returning an end here i don't have to return a task like we did in the
in the previous example because we've marked the method with async we want to return the task type
so in the get gas i'm going to return an int so async does that for me it's some syntactical sugar
that allows me to re write the method like it was a sequential method and return the normal value
and it wraps that with the task the other thing that the async method does is everything after the
await is is effectively a continue with almost now if you declare a method as async you need
to have at least one a weight within the method if you don't have any awaits then
then there's no opportunity to make the method a task or asynchronous so what happens when we call
a wait well remember earlier i mentioned i could call wait or result to just block and wait for a
the answer for this task to complete so that i can return its result well that's partially what a
weight does when when i put a weight uh keyword in front of a task that first of all says we're gonna
block we don't continue this execution until that task has completed so there so this synchronizes
things so because of the await we're not going to announce that we're finished washing the car
until this task has actually completed but here's what's very different about this
and our continue with that i did previously await and async actually return control to
main or to whoever called this method in our case main so here we're going to return
this task uh it's actually not exactly this task they've actually wrapped it up in a larger task
because it also has to include the remainder of the method okay so immediately right here if this
if this is not finished yet we're c-sharp is going to create a task that's encapsulating our task
plus the remainder of the method and return that object to the caller i've made the same changes to
the other methods where we are awaiting the result so result has to be acquired before i can display
it later on or return it from this method so again writing the method in a much more sequential
looking way i'm not having to do the continue with and all of that myself inside main you
see pretty much the same code i showed you in the previous example because we're getting tasks back
and i'm putting them into a list and then we've got our loop where we wait on the on on any task
to complete and then and then go from there all right so let's uh let's run this a couple of
things that are interesting a lot of this kind of starts off the same way as the previous examples
uh thread number one the main thread is the thread that steps into our different uh
things get gas car wash and so forth those execute up to the weight as thread number one then inside
let's take the car wash task uh it's assigned thread number seven to begin work okay and then uh
washed car was must have been a pretty long random number because eventually that finishes and then
notice that inside main in our loop when we finish up we're also running under that thread
id and then main the final statement of main way down here we finish up with thread id number
seven rather than thread id number one and so the same thing with the other task you'll notice that
the whatever the thread id that was assigned to the internal task is the also the thread id
that is executing the remainder of main so just like the await keyword in the mechanic methods
await keyword that i've added in main waiting for any of the tasks to complete in the previous
example i had dot result that's why we stayed on we just blocked on the result and waited for
that result to come in and continued executing we blocked thread one but now that there's an
await keyword and this main is essentially an async at this point we're going to return
control to whoever called this method and and it's the real main that calls this method with the
weight so back here is thread one and uh we call main async still thread one so we're thread one
for a little while but as soon as we hit the await we essentially return that object
and then we call weight to block that so thread one now is blocked waiting for the remainder of
this method to finish so because of the await all of the rest of the the remainder of this
method will execute with whatever task happens to be available out of the thread pool and when
all of those have completed then our weight is triggered to say yes the task is now finished
so it's it's an interesting you know finally getting down to this closed curly brace
allows weight to return so an interesting difference when using async and a weight not only
are they a little bit easier to use because i don't really have to
chain uh continue with and and group my tasks up i also get an additional benefit in that i get a
much more granular opportunity to keep threads executing through the cpu and and
not have a bunch of threads idle and eliminate the benefits of the multi-threaded environment i hope
that that answered some questions about a sink and a weight if you like this uh hit to subscribe
so you don't miss out on any other videos and if you like this one check out this video