C# Async/Await/Task Explained (Deep Dive)
Summary
TLDRThis video script offers an in-depth exploration of asynchronous programming in C#, focusing on the concepts behind async and await rather than just their implementation. The presenter uses the analogy of making tea to explain how async operations work, highlighting the difference between synchronous and asynchronous tasks. The script delves into the technical aspects, such as the role of the task, the state machine created by async, and the checkpoints introduced by await. It aims to clarify the underlying mechanisms that allow for non-blocking code execution, making it easier for viewers to understand and utilize async/await effectively in their C# applications.
Takeaways
- 😀 The presenter aims to clarify the concepts behind async/await in C# rather than just showing implementation.
- 🔍 The video uses the analogy of making tea to explain asynchronous programming, emphasizing non-blocking operations.
- 💡 It's highlighted that async/await allows for multitasking by freeing up the thread to do other work while waiting for external tasks to complete.
- 🛠️ The script explains that once a task hits an await, it yields control back to the thread pool, allowing other tasks to run.
- 🔄 The concept of a 'task' is introduced as a bridge between the state machine and the await checkpoints.
- 📚 The async keyword is described as spawning a state machine, which is a key component of asynchronous programming.
- 🔑 The await keyword is likened to checkpoints within the state machine, controlling the flow of execution.
- 🖥️ The script provides insight into how the .NET runtime manages threads, emphasizing the role of the thread pool.
- 🌐 It discusses how network operations are handled asynchronously, involving the operating system and network drivers.
- 🔍 The presenter uses ILSpy to demonstrate how async methods are translated into a state machine by the compiler.
Q & A
What is the main focus of the video?
-The main focus of the video is to explain the underlying concepts behind async and await in C#, rather than just the implementation.
Why does the presenter believe that understanding async/await can help with sleepless nights?
-Understanding async/await can fill gaps in knowledge that might cause confusion or keep one awake at night, implying that it's a concept that can be puzzling if not fully grasped.
What analogy does the presenter use to explain async/await?
-The presenter uses the analogy of making a cup of tea to explain async/await, where boiling water is a synchronous task and preparing the tea while the water is boiling is an asynchronous task.
How does the presenter describe the difference between synchronous and asynchronous operations?
-The presenter describes synchronous operations as tasks that are completed by the same thread without waiting for external processes, while asynchronous operations allow the thread to be free to do other tasks while waiting for an external process to complete.
What is a 'task' in the context of the video?
-In the context of the video, a 'task' is an object that represents an asynchronous operation and is the bridge between the state machine created by async and the await keyword.
What is a 'state machine' as explained in the video?
-A 'state machine' in the video refers to the object that is created when a method is made asynchronous. It holds the state of the method across await calls.
What is the role of the 'await' keyword in asynchronous programming?
-The 'await' keyword is used to pause the execution of the current method until the awaited task is completed, allowing other operations to run in the meantime.
How does the presenter demonstrate that a task can be completed by a different thread?
-The presenter demonstrates that a task can be completed by a different thread by creating a long loop that runs in parallel while an asynchronous task is awaited, showing that the task can finish on a different thread while the loop is still running.
What is the purpose of the thread pool as explained in the video?
-The thread pool manages the execution of tasks by distributing them across available threads, allowing for efficient use of system resources and preventing thread overload.
How does the presenter explain the concept of a state machine checkpoint?
-The presenter explains that a state machine checkpoint is created by the await keyword, which divides the asynchronous method into parts that can be executed sequentially as different states of the state machine.
What does the presenter suggest is the key to understanding asynchronous programming?
-The presenter suggests that understanding the task as a bridge between the state machine and the code, the async keyword as what spawns the state machine, and the await keyword as defining the checkpoints within the state machine is key to understanding asynchronous programming.
Outlines
🍵 Introduction to Async and Await
The speaker begins by introducing the topic of asynchronous programming in C# using the analogy of making a cup of tea. They explain that async/await is about handling tasks that take time without blocking the execution of other code. The presenter emphasizes the importance of understanding the underlying concepts rather than just the implementation. They set the stage for a deep dive into how async/await works, starting with the basics for those unfamiliar with the concepts, and promising to fill in knowledge gaps that might be causing confusion.
🔄 Transitioning to Asynchronous Code
The speaker illustrates the shift from synchronous to asynchronous code by continuing the tea-making analogy. They explain how, in synchronous code, you would have to wait for the water to boil before proceeding with other steps, whereas in asynchronous code, you can start the kettle and then immediately proceed to take out the cups and add tea, waiting for the water to boil in the background. The explanation includes the concept of a 'task' and the use of 'await' to pause the current method until the asynchronous operation is complete. The summary also touches on how asynchronous code propagates from the bottom up in a codebase.
🌐 Understanding the Underlying Mechanisms
The presenter delves into the mechanics of asynchronous programming by discussing the role of the task, the state machine, and the awaiter. They use the tea-making analogy again to explain how the computer can handle external processes in parallel, similar to how one can multitask in making tea. The explanation includes the concept of the thread pool and how it manages threads, allowing a thread to be free to do other work while waiting for an asynchronous operation to complete. The summary also includes a discussion on how the application resumes execution on a different thread once the asynchronous operation is done.
💻 Exploring the State Machine and Async/Await
The speaker provides a deeper look into the state machine generated by the async keyword and how the await keyword checkpoints within it. They use the analogy of boiling water and adding tea to explain how the state machine preserves state and allows the program to resume execution from where it left off. The explanation includes how the .NET runtime manages the state machine and how the thread pool resumes execution of the function on a different thread once the asynchronous operation is complete.
🔧 Practical Example and Further Explanation
The presenter provides a practical example of using async tasks with an HTTP client to fetch a webpage from Google, explaining how the thread pool manages thread execution. They discuss how the operating system schedules threads on different processors and how threads can jump between CPUs. The explanation reinforces the role of the thread pool in managing threads and how it hands them off to the processor for scheduling. The summary concludes with an invitation for viewers to engage with the content, ask questions, and look forward to future videos on the topic.
Mindmap
Keywords
💡async/await
💡Task
💡State Machine
💡Thread Pool
💡Synchronous
💡Asynchronous
💡Await
💡.NET Runtime
💡Intermediary Language (IL)
💡Checkpoint
💡Thread
Highlights
Introduction to async/await in C# with a focus on underlying concepts rather than just implementation.
Explanation of async/await using the analogy of making a cup of tea.
The difference between synchronous and asynchronous operations demonstrated with boiling water.
How to make tea asynchronously by using 'await' to pause and resume tasks.
The necessity of understanding the task, the bridge between the state machine and the await.
The role of the thread pool in managing and resuming tasks.
How the async keyword transforms a function into a state machine.
The importance of the 'await' keyword as a checkpoint within the state machine.
Explanation of how tasks are completed by different threads in parallel execution.
The concept of a state machine having multiple parts controlled by the 'await' keyword.
How the thread pool resumes execution of a function at a different thread.
The illustration of the state machine's transition through different states with each 'await'.
The impact of the async/await pattern on the generated intermediate language (IL) code.
How the state machine preserves state across asynchronous operations.
The role of the operating system in scheduling threads and the thread pool's role in managing them.
The practical demonstration of how threads can jump between CPUs.
Encouragement for viewers to apply the concepts learned to enhance their understanding and use of async tasks.
Transcripts
welcome everyone today I'm gonna be
covering a sink and a weight along with
tasks in c-sharp and generally today I'm
gonna be explaining about a sink and the
weight and how it works so if you come
to understand just async/await
from any other programming language you
may benefit here I've seen a lot of
tutorials that explain the
implementation rather than explaining
really the underlying concepts behind a
sink and weight so today I'm gonna be
primarily focusing on how it really
works rather than how to implement it
okay so that will really let you
understand it and hopefully fill some
gaps in your knowledge which don't let
you sleep at night okay and the reason I
guess why you're actually watching this
video is there are so many of the
tutorials out there why are you still
looking at tutorials right you want to
know some but today we're gonna take a
deep dive and we're gonna start again if
you don't know anything we're gonna
start with an analogy all these videos
have an analogy and here is mine right
we're gonna make it be making cup of tea
make a cup of tea you need to boil the
kettle take the cups out put the tea in
the cups and then put the boiled water
in the cups right simple well let's see
to make the tea
we first need to boil the water this
will give us some water good so then we
take the cups out we dump them and then
put tea in cup all right and then we
pour let's say we pour our water and
cups
this will give us our tea and then we'll
be able to return tea hey a couple of
simple steps
now the boiling water part this is the
part where we press the button on an
external thing to me so when I'm doing
something I am completely synchronous I
may have a couple of multiple threads
that may put me in in a delusion that I
feel like I'm multitasking but not
really right oh boy water start the
kettle and then we want to delay for two
seconds our our kettle is really fast we
boil in two seconds right we want to get
a waiter get results to make this
perform synchronously this is where
we're waiting for the kettle we dump it
and then get all finished boiling and we
want to return water okay so let's go
ahead and run this let's see what
happens
cool so starting the quatre kettle
waiting for the kettle kettle finished
boiling take the cups out but the team
cups and pour water in cups how could
this be better how well I'm a human
being I'm no way am I gonna wait for the
kettle to boil before I take the cups of
tea out and put the tea in them right so
something I do at the same time right
because the kettle is external to me it
can work while I'm doing another thing
right I don't think I am NOT the kettle
there's a key difference here and this
is exactly how the computer does
whenever it's something external to the
process itself it can go work on its own
it doesn't need to wait for it okay so
this is the whole thing behind a
synchrony so let's go ahead and make
this asynchronous I'm going to copy this
these two functions boil water async and
we'll do make tea async as well
sink so here instead of get a way to get
a result we're gonna put a weight here
and we will need to do a sync task so
what's gonna happen here is once the
thread is executing here it's gonna once
it hits a weight it's gonna let up
control it's gonna basically the thread
is gonna get go free and it's gonna go
do something else
and then the thread pool is basically
once it receives a signal that this
delay is over it's gonna pick another
threat to resume the task okay but we're
gonna take a look at how that works in a
little bit later but let's go ahead and
finish working this example so oil water
we actually want to call boil water
async now and what we get is a task this
is a boiling water task okay and what we
actually want to do is once we have
finished putting T in our cups we want
to get the water by awaiting boiling
water okay and again for this we'll need
to make this an asynchronous task and
we're just gonna go over what task is in
a second okay and one thing to notice is
once we go the asynchronous path we are
no longer allowed to have asynchronous
just in one sort of place it propagates
through our our code from the bottom up
up up and up to the main okay and again
this is something that we'll need to
make Anna synchronous tasks and the way
so let's go ahead and run this cool
right so start the kettle a wait for the
kettle so we're waiting for the kettle
here take the cups out put 10 cups we
wait for the kettle to finish boiling
and pour water and cups so we have
successfully achieved a synchrony for
boiling the water what's the next step
well the next step is actually
understanding what the [ __ ] is actually
going on right so we have the task we
have a sink and we have a wait we have
three components to really understand
the task
is the bridge between the state machine
that async creates and the weight is the
checkpoints within your state machine
once so this is a really my elevator
pitch of this whole asynchronous
programming right if you know how to use
this and you've been using this for a
while that whole sentence might have
cleared everything up for you otherwise
if you don't know about the state
machine or you don't understand what I
mean by a weight is a checkpoint let's
go ahead and dive deeper so what's going
to happen if I want to make the water
finish boiling before I put the tea in
the cups right so somewhere in the
middle here let's go ahead and prove
that another thread is indeed going to
complete the water boiling process right
so it's going to happen in parallel on
another thread okay so vari let's go
ahead and make a for loop really really
big loop and in here we are going to
just add I to eight or this amount of
time so now this is a really really long
loop what I'm gonna do is I'm also going
to reduce the amount of time it takes
for my kettle to boil and yeah let's go
ahead and run this and this is what
we're gonna see now so you can see there
is no waiting here this thread is
working full-time its throttling it's
going through this loop like right but
what has happened is when we have take
the cups out the kettle has finished
boiling while we were spinning around in
one place maybe we'll praying over the
dog whatever kettle finished boiling and
it finished on a different thread okay
and then we can put the tea in the cups
and then we grab the result from that
hurt right right because we can't we
just have the water here so
with that knowledge how does the
application figure out how to make a
different thread resume execution within
this function right it's a function that
essentially we've gone through the half
of it and we kind of went well we have a
week let's exit it right and then while
we were still working here we resumed
execution in this function what happened
right what is going on for this I'm
gonna take a little bit more of a
realistic example of how we use threads
right so let's go ahead and get some
visibility on these threads so we're
gonna be grabbing the current thread and
we're just gonna be outputting the
managed ad and we're just gonna dump it
and we're gonna have a couple of
processes the first one we're gonna
create a client and it's going to be an
HTTP client wrong place new HTTP client
once we have the HTTP client let's
actually create some work that we need
to do we need to do right so a task and
we're going to get string async and
we're going to go to Google okay so in
between here I'm gonna do the same thing
I'm just gonna throw all this thread a
little bit
okay
now I want to get the page I want to get
the page from the task right because
we're going to Google I want to wait on
the task and I want to actually get the
page so let's run this everything runs
on one thread so this is what's going to
happen here once we get the water it's
still going to be running on the same
thread this task is actually completed
by a different thread okay so the way we
can do this is we can reduce a couple of
zeros on this loop and we can run this
and we will see that the the rest of
this function is now completed by a
different thread okay so how does this
work and this is something that the
thread pool allows us to do so for this
I'm gonna bring in the imagery that I
have drawn I'm a bit of an artist myself
so here we have the main on the main
right our our program that we have just
written right here and it's not just
that the computer takes our program in
executing our program sits in the.net
runtime the.net process is being started
and it's kind of this harness that our
program is sitting in so our code runs
this but when we issue the getstring
async what actually happens is we go to
the operating system and we're telling
it we need to make an internet call we
need to make an a call to the internet
and this is where the operating system
is gonna basically say right oh let me
go to this network driver so depending
on what computer you have you're gonna
have a different operating system and
different network drivers right so this
needs to be like an abstract concept
that we just call a function on an
operating system and then the operating
system is gonna have a specific driver
for a specific network card and it's
gonna call the specific function okay
the network driver people the people who
implement the network driver have to
implement the correct function for it to
be able to operate with the opera the
Windows operating system and vice versa
for other operating systems oh yeah when
we scheduled this task when we say we
need to make a network call
so this is something that happens
synchronously it make the operating
system makes a call to the network
driver and it says go get me this page
but it also gives it an async flag so
what the async flag does is basically
the network driver frees controller the
thread that called it okay so the so we
don't get the controller thread back
from a operating system perspective it's
the network driver that basically says
ma'am off you go right you're free to go
and then once we return to step three I
don't know exactly if when we return to
step three but while we resume execution
of our code the network driver is gonna
send off the request and is gonna he's
gonna try to get the page right so while
he is getting the page we are throttling
through this loop we're like right
spinning her out and then we get to step
four and this is where the magic happens
right this is where the split and
threads hop and threads happen this is
where thread one basically flies off and
goes there's something else this is
where when we await the application
basically goes over here to a thread
pull the thing that manages your dotnet
threads and it asks it you know this
tasks though the the way scheduled is a
complete and the thread is gonna and the
thread pool is gonna go no it's not and
this is when we basically we don't block
the thread here either the thread that
was running that process this function
returns to the thread pool and the
thread pool can use that thread for
something else while this is as I said
this is sleeping so now that we know
that this thread has gone away and it's
doing something else how do we return to
finish off this function
well once the network driver gets its
data back it wakes up it goes like huh
dude you know that task you asked for
here it is take and then the thread pool
goes oh okay this function carry on
running okay and we complete the
function so the next thing to understand
is how can the task pool essentially
stop in the middle of the function and
then resume execution at a different
thread how's that possible
the next thing to understand is
essentially the async keyword spawns the
state machine so if I run this code
right here here's some intermittent
language that the common language
runtime it uses to run as soon as we add
a sync keyword it will bluster the
amount of code generated and essentially
we get this code explosion which is the
state machine okay so what I'm alluding
to is that this function is no longer a
function it becomes an object which is
the state machine right which sits in
the dotnet runtime so let's go ahead
peek into this state machine and take a
look at what it's made of
hey for this I'm gonna go into visual
studio and here essentially I'll have
this little example where I have the H
the same example so I have the HDTV
client I call Google I have the loop
I await on that call then I have the
page and then I'll put hello world okay
so what I'm gonna be using here is
called I else pipe basically take a look
at the code at the intermittent language
generated code for this function but in
its readable format so the first thing
to notice is that this main function is
now an object right it's no longer a
function once an object
and it's fields are httpclient the task
now we're
turning here basically the variables in
the function I've turned to a fields on
the object right so it's some state that
is going to be preserved on this state
machine a and the primary function where
basically the things that we do with our
variables is now but in to move next and
it's check pointed by the awake keyword
okay so when I say check point at what
I'm trying to do know is that here we
have the first part of our code and once
we pass the awake keyword we have the
second part and every subsequent awake
keyword we get an additional part to we
give an additional part to the state
machine to which it can jump on or an
additional state that the state machine
can be in okay so back to the IL code
let's go ahead and take a look at the
move next function this is how we change
the state as a state machine and this is
how we move through the parts move next
function what I want to take what I want
to bring your attention to are these
parts so the first part is this if
statement where we initialize the
httpclient we get the string acing this
is where we make the call to the network
adapter we do our while loop and then we
check if the task is completed right
let's forget about this first and go to
the step where everything is executed in
one thread if we go past this we skip
this else because it's out of scope and
we get there waiter that we set here and
we get the page we then clear the waiter
and we output hello world right
so here the await right here these two
parts are part two okay so part one part
two you can see that the page is being
set here
this is where part two begins and where
the loop ends that's where part one ends
hey so if it's not complete this is
where we plain out just returned from
the function but before we return we
make sure to set some State on the state
machine so what I want you to understand
is that when we create the state machine
we put it in memory and RAM right here
somewhere
and it's not garbage collected so the
next so when we have this call that's
going back to our thread pool the way it
resumes it is the thread pool can find
the state machine and memory and then
just call the function move next again
and because the state is now set to zero
the number is not going to be zero we're
going to essentially skip it right here
we're gonna enter this clause and this
is where we're just basically again
we're gonna get the result of the await
err because now we don't need to check
if it's complete
we know it's complete because the thread
pool has triggered this call okay and at
that point we go past this and we set
the result and the task is complete our
program finishes okay and that's pretty
much it now what I want to do is I
essentially I want to add another
checkpoint so here let's go ahead turn
this to a wait here and instead of page
we're gonna have task two and we're
still gonna have hello world so
splitting this into parts it will be
something like this first part second
part third part okay let's go ahead and
take a look at what gets generated now
because it's gonna be slightly different
and it's gonna be quite interesting so
let's take a look at I'll spy
we want to find open go okay so in here
find async/await program main
okay so again all the variables are here
so just the task task - a HTTP client
good and now these are just strings
right and so looking into the move next
what we get now are the two waiters that
we potentially want to store and what I
want to do here is essentially recreate
or get an understanding of where these
parts are again okay so we're gonna
enter here we're gonna skip this first
if statement because basically what I'm
looking for is this HTTP client right
because that's the first thing that we
have defined I know that's the first
thing that's gonna get executed so we
have the HTTP client and then we go to
the network card and we say go get us
something it's not gonna be completed so
we set the state we return next time we
come back I want to look for this task
being completed so the task that we're
setting here is a waiter - so let's go
ahead and look for a waiter - somewhere
here in the bottom here we're grabbing
the waiter - from the state we're
getting the result getting the task
setting some parameters we're looping
and then we're doing the second call and
again this is again when we return out
of part two this is where part two
finishes hey after we've done the
looping we exit so third time when we
come back right when the second network
card call goes to the thread boom right
I know I just got you this page
right again we move for next and we can
take note of where we exited here we set
the stake to one so this is where the
stake machine uses go to statements and
through the move next it equals one yeah
it's not zero if one yep this is where
we go we get the ax waiter and again
this is the waiter that we've set
previously before exiting as the state
machine we go to this label here and we
can see that it's this label down here
and this is where we again get the
result of the a waiter because we
already know it's complete because the
thread pull knows it we don't need to
check that is complete and we output
hello world so this is where we execute
the third part and I apologize this is
probably a little bit something like
this in terms of parts yeah so that's
pretty much how I interpret the task as
the bridge between the state machine and
your code async is what spawns the state
machine so you need the task to
interface with it and then you have the
await as in terms of how many parts do
you have to your state machine how many
times is it gonna get called again okay
one thing I wanted to maybe clear up if
you're not sure how we were actually
executing in parallel when we were
throttling right here right
because what we have is in my task
manager for example I have 4 cores 4
cores that can execute if for example on
this first core that I'm hovering over
I've been doing the loop the insanely
long loop so it's throttling through
here but the task that's gonna get
picked up the thread pool it's not gonna
wait for this first processor to finish
it's gonna take this thread pull and
it's gonna throw it to the operating
system and the operating system is then
going to schedule it on the correct
processor so the operating system is
taking care of the heavy lifting the
thread pull is just basically takes care
of regulating how many threads you have
and basically handing them off to the
processor and the processor does the
scheduling on your CPU and also while
your thread is executing because all
threads are all are allocated a certain
amount of time on the CPU what actually
can happen is that the thread can
execute on one CPU for a long time so
let's say this takes 2 seconds to run
for the
second it's gonna execute on this CPU
and then for the second second it's
gonna execute on a different CPU right
so threads are not bound to a CPU they
can jump between CPUs and just
understand that the thread pool is what
manages these threads okay so hopefully
this was a good enough explanation of
the async task and a wait hopefully you
understand it a little bit more now if
you do and you did enjoy this video
leave a like subscribe if you have any
questions make sure to leave them in the
comment section don't forget to join the
discord server I'm gonna make another
video on how to actually use the async
tasks and maybe give you a little bit
more understanding of the potential that
you can achieve with these if this video
hasn't already done that yeah thank you
for watching and hopefully I'll see you
in my other videos
Посмотреть больше похожих видео
Asynchronous Programming in C# Explained (Task.Run, Task.WaitAll, Async and Await)
Asynchronous JavaScript in ~10 Minutes - Callbacks, Promises, and Async/Await
Asynchrony: Under the Hood - Shelley Vohr - JSConf EU
Node.js Tutorial - 42 - Event Loop
JavaScript Visualized - Promise Execution
Javascript Promises vs Async Await EXPLAINED (in 5 minutes)
5.0 / 5 (0 votes)