Asynchronous Programming in C# Explained (Task.Run, Task.WaitAll, Async and Await)

Frank Liu
8 Nov 201923:46

Summary

TLDRThis script explains asynchronous programming using a relatable analogy of moving house and doing chores. It contrasts synchronous and asynchronous programming with C# examples, demonstrating how tasks can run in parallel without waiting for each other. The script further clarifies scenarios where tasks depend on each other, using 'Task.WaitAll' and 'await' keywords to synchronize. It also touches on the practical use of callbacks and async/await for a more straightforward, synchronous-like coding experience in asynchronous programming.

Takeaways

  • 🏠 **Asynchronous Programming Defined**: Asynchronous programming allows for tasks to be performed without waiting for the completion of other tasks, similar to how you can pack while your house is being cleaned.
  • 💻 **Synchronous vs Asynchronous**: Synchronous tasks must be completed in a specific order, whereas asynchronous tasks can be done in parallel without waiting for each other.
  • 🛠️ **C# Implementation**: In C#, asynchronous programming is implemented using tasks, which can be run concurrently without blocking the main thread.
  • 🔄 **Task.Run Usage**: `Task.Run` is used in C# to execute functions asynchronously, allowing for non-blocking code execution.
  • 🔄 **Simulating Long-Running Tasks**: The `Sleep` method is used in C# to simulate long-running tasks, which is useful for demonstrating asynchronous behavior.
  • 🔄 **Order of Execution**: The order of task execution in asynchronous programming can vary, as seen with the different outputs when running tasks concurrently.
  • 🔗 **Dependency Management**: When tasks depend on each other, `Task.WaitAll` or `Task.WaitAny` can be used to ensure that dependent tasks are completed before proceeding.
  • 🔄 **Callbacks in Asynchronous Programming**: Callbacks are used in C# to handle the results of asynchronous tasks once they are completed.
  • 🔄 **Async and Await Keywords**: The `async` and `await` keywords in C# are used to simplify asynchronous programming, making it appear synchronous while still running tasks in parallel.
  • 🔄 **Control Flow with Async/Await**: Using `async` and `await` allows developers to write asynchronous code that looks like synchronous code, maintaining a clear sequence of operations.
  • 🔄 **Static Methods and Async**: Static methods can also be made asynchronous by using the `async` keyword, allowing for non-blocking calls within the method.

Q & A

  • What is asynchronous programming?

    -Asynchronous programming is a programming paradigm where tasks can be executed concurrently without waiting for the completion of other tasks. It allows for tasks that are independent of each other to run in parallel, improving efficiency and performance.

  • How does the analogy of moving houses relate to asynchronous programming?

    -The analogy of moving houses is used to illustrate the concept of asynchronous programming. Just as you can start packing your belongings while your new house is being cleaned or painted, asynchronous programming allows you to start new tasks while others are still running, without waiting for them to complete.

  • What is synchronous programming in the context of the script?

    -Synchronous programming refers to the execution of tasks in a sequence where one task must complete before the next one begins. In the script, it is used to contrast with asynchronous programming, where tasks can be executed concurrently.

  • How is asynchronous programming implemented in C#?

    -In C#, asynchronous programming is implemented using the `Task` class and the `async` and `await` keywords. The `Task` class allows for the execution of operations on separate threads, while `async` and `await` provide a way to write asynchronous code that looks and behaves more like synchronous code.

  • What is the purpose of the `Task.Run` method in C#?

    -The `Task.Run` method is used to execute a method asynchronously. It allows the calling code to continue executing without waiting for the task to complete, which can improve the responsiveness of an application.

  • Why is the `await` keyword used in C#?

    -The `await` keyword is used to pause the execution of the current method until the awaited task is completed. It allows for the continuation of the method after the awaited task has finished, providing a way to handle asynchronous operations in a more synchronous-like manner.

  • What is the difference between `Task.WaitAll` and `Task.WaitAny` in C#?

    -`Task.WaitAll` is used to wait for all specified tasks to complete, while `Task.WaitAny` waits for any of the specified tasks to complete. In the script, `WaitAll` is used when a task depends on the completion of multiple other tasks.

  • How can you handle dependencies between tasks in C#?

    -Dependencies between tasks can be handled by waiting for the completion of prerequisite tasks before starting a dependent task. This can be done using `await` to wait for the task to complete and then using its result as needed.

  • What is a callback function in the context of asynchronous programming?

    -A callback function is a function that is called after another function has completed its execution. In the context of asynchronous programming, it is used to handle the result of an asynchronous operation once it has completed.

  • How does the `async` keyword simplify asynchronous programming in C#?

    -The `async` keyword allows you to write asynchronous code that looks and behaves more like synchronous code. It enables the use of `await`, which allows you to pause the method execution until an awaited task is completed, making the code easier to read and maintain.

  • What is the significance of the order of task execution in the script?

    -The order of task execution in the script demonstrates the non-deterministic nature of asynchronous programming. Since tasks can run concurrently, the order in which they complete and their results are printed can vary, depending on the duration of each task.

Outlines

00:00

📚 Introduction to Asynchronous Programming with Real-Life Example

The speaker introduces asynchronous programming by comparing it to real-life situations. They explain how certain tasks need to be done in a sequence, like moving houses, while other tasks can be done concurrently, like cleaning and packing. This sets the stage for understanding synchronous vs. asynchronous programming in C#. Synchronous programming is compared to tasks that must happen in order, while asynchronous programming allows tasks to run simultaneously.

05:00

💻 Demonstrating Synchronous Code Execution in C#

The speaker describes synchronous execution in C# using a simple example with three calculation functions. The functions are run one by one, demonstrating that the output appears in the exact sequence they were called. The key point is that these calculations do not depend on one another, and thus could run in parallel if done asynchronously. This section establishes the basic understanding of how synchronous code behaves.

10:05

⚙️ Transforming the Code to Asynchronous Execution

The speaker introduces C#'s task-based asynchronous programming. They demonstrate how to run the calculation functions asynchronously using `Task.Run`, leading to a different order of output. Since one calculation function simulates a longer-running task, the output order changes as faster functions complete first. This section emphasizes the non-blocking nature of asynchronous tasks and explains how parallel execution works.

15:06

🔄 Handling Task Dependencies in Asynchronous Code

The speaker explains how to handle dependencies between asynchronous tasks. Using a scenario where one calculation depends on the results of others, they introduce the `Task.WhenAll` method. The code ensures that certain calculations wait for others to complete before proceeding. This section shows how to maintain control over asynchronous tasks when outputs need to rely on prior results, ensuring correct execution order.

20:08

⏳ Managing Asynchronous Callbacks and Task Continuations

This section discusses using callbacks in asynchronous programming. The speaker explains how to trigger tasks to run after others have completed, using callbacks to pass results between tasks. They demonstrate how the asynchronous flow can still control task execution order, even when tasks run in parallel. This highlights how asynchronous tasks can remain dependent on each other while not blocking other executions.

🔀 Using `async` and `await` for More Intuitive Asynchronous Code

The speaker introduces the `async` and `await` keywords, simplifying asynchronous code to resemble synchronous programming. They create a new function using these keywords, explaining how `await` pauses the function execution until a task is complete. This allows developers to write code that looks sequential but runs asynchronously, making asynchronous programming easier to follow and maintain.

🔗 Final Thoughts on Asynchronous Programming

The speaker wraps up by combining all the previous concepts into a final example where tasks are run asynchronously but still follow a defined sequence using `async` and `await`. They emphasize how asynchronous code can manage both independent and dependent tasks. The section concludes with a demonstration of how this technique allows developers to control execution flow while benefiting from non-blocking, asynchronous behavior.

Mindmap

Keywords

💡Asynchronous Programming

Asynchronous programming is a paradigm where a program can perform tasks concurrently by dividing them into separate processes that may run independently and concurrently. It's a way to write programs that can handle multiple operations at the same time without waiting for one to finish before starting another. In the video, the concept is introduced by comparing it to daily life activities that can be done in parallel, like cleaning a house while packing. It's also illustrated with C# code examples where tasks are run in parallel without waiting for each other to complete.

💡Synchronous Programming

Synchronous programming is a programming model where tasks are performed one after the other in a linear sequence. This means that each task must wait for the previous one to complete before it can start. In the video, the concept is contrasted with asynchronous programming by using the example of moving out of an old house before moving into a new one, which must be done in a specific order.

💡Task

In the context of the video, a 'Task' refers to a unit of work that can be executed asynchronously in C#. Tasks are used to run operations in the background without blocking the main thread of execution. The video explains how to use tasks to run functions asynchronously, allowing for more efficient use of resources and potentially faster program execution.

💡Sleep

In the video, 'Sleep' is used within a C# function to simulate a long-running process. It's a method that suspends the execution of the current thread for a specified interval. The script uses 'Sleep' to demonstrate how a task might take longer to complete than others, thus affecting the order in which tasks appear to run when executed asynchronously.

💡Callback

A callback in the video refers to a function that is called after another function has completed its execution. It's a way to handle asynchronous operations where you want to perform some action once an operation is complete. The video uses callbacks to explain how to deal with tasks that depend on the results of other tasks, showing how to wait for a task to complete before proceeding.

💡Await

The 'await' keyword in C# is used to pause the execution of an async method until a task is completed. It allows for writing asynchronous code that looks and behaves a bit more like synchronous code, making it easier to read and understand. The video explains how 'await' can be used to wait for a task to finish and then proceed with the next steps in the program.

💡Async

The 'async' keyword in C# is used to define an asynchronous method. It allows the method to be paused and resumed, which is useful for performing asynchronous operations without blocking the main thread. The video discusses how 'async' can be combined with 'await' to create a more straightforward asynchronous programming model.

💡Concurrent

Concurrent refers to the ability of a system to handle multiple tasks or processes at the same time. In the video, concurrency is a key benefit of asynchronous programming, allowing for more efficient use of system resources and potentially faster execution of programs. The script uses examples of tasks running concurrently to demonstrate this concept.

💡Dependency

Dependency in programming refers to a situation where one piece of code relies on the output or completion of another. In the video, dependency is discussed in the context of tasks that need to wait for other tasks to complete before they can run. The script provides examples of how to handle dependencies using tasks and the 'await' keyword.

💡Main Function

The 'Main' function in C# is the entry point of a console application. It's the first method that gets executed when the program starts. The video uses the main function to demonstrate how synchronous and asynchronous code can be executed, showing how the order of execution can differ between the two approaches.

💡Thread

A thread is the smallest sequence of executable code, and it's the smallest unit of processing that can be scheduled by an operating system. In the video, threads are implied when discussing asynchronous programming, as tasks can be executed in parallel on different threads, allowing for true concurrency. The script explains how tasks can run in the background, suggesting the use of multiple threads.

Highlights

Introduction to asynchronous programming and its comparison to daily life activities.

Explanation of synchronous programming with an example of moving house.

Illustration of how certain tasks can be done in parallel, like cleaning and packing a house.

Introduction to asynchronous programming in C#.

Explanation of how to implement asynchronous programming in C# using tasks.

Demonstration of running functions asynchronously with `Task.Run` and `async`/`await`.

Example of how the order of execution can change with asynchronous programming.

Explanation of how to handle dependencies between asynchronous tasks.

Use of `Task.WhenAll` to wait for multiple tasks to complete.

Example of a callback function in asynchronous programming.

Introduction to the `async` and `await` keywords in C#.

Explanation of how `async` and `await` can make asynchronous code look synchronous.

Example of creating an asynchronous function that waits for other tasks.

Demonstration of how to chain asynchronous tasks using `await`.

Explanation of how to create a static method that awaits the completion of other tasks.

Example of how to use `await` to control the flow of asynchronous tasks.

Final thoughts on asynchronous programming in C# and its practical applications.

Transcripts

play00:00

hello everyone today I want to talk

play00:03

about asynchronous programming first of

play00:08

all what is asynchronous programming so

play00:11

without going to the code let's examine

play00:15

the daily life some of the actions we

play00:18

can take one by one we have to take them

play00:21

one by one

play00:22

for example we buy a new house and we

play00:26

move out of we move out from the own

play00:29

house and we move in to the new house we

play00:33

can't we have to follow the sequence of

play00:36

this water we can't move out before buy

play00:40

a new place otherwise we don't have any

play00:42

place to to live and we can't move in

play00:47

before we move out but some of the

play00:50

actions we don't have to take them in a

play00:55

particular order

play00:55

for example after we we bought our new

play01:01

house we need to ask someone to clean it

play01:04

up to paint the wall or maybe we want to

play01:09

make a new kitchen so we want to clean

play01:11

that clean up the house but at the same

play01:14

time we can start packing we don't have

play01:19

to wait till the cleaning lab is

play01:22

finished before we start packing we can

play01:26

do that to them at the same time so this

play01:33

in computer on language we can call it

play01:40

asynchronous programming whereas this is

play01:44

called synchronous programming right

play01:48

let's take a look at the c-sharp codes

play01:51

how asynchronous programming is

play01:55

implemented in c-sharp

play01:58

so before again before we examine how a

play02:02

king Sinclair's framing works in c-sharp

play02:04

let's take a look the codes that that

play02:07

runs normally synchronously so we have

play02:13

three calculation functions it doesn't

play02:17

do any calculation just returns number

play02:21

and the first one we're using sleep

play02:24

throughout the sleep to simulate the

play02:27

situation where this calculation takes a

play02:31

longer time than the others and we have

play02:34

this this is this is a calculation just

play02:36

cause the other ones and it's called in

play02:39

the main function that it's the entry

play02:41

point of the console application so if

play02:43

we run this application the lines of

play02:48

code here will run one by one in this

play02:52

water the water will not be messed up

play02:56

has to be the SEC the first one runs and

play02:59

then the second one and there's third

play03:01

one so

play03:16

as you can see it says calculating

play03:20

result one calculation without two

play03:22

culinary's all three so if we look at

play03:27

the the calculation the three

play03:30

calculation functions carefully we can

play03:35

see that they don't actually depend on

play03:37

each other

play03:38

right they don't depend on each other at

play03:44

all so there's no reason why we cannot

play03:47

let them run in parallel and how do we

play03:51

do that in c-sharp we have this

play03:54

asynchronous programming functionality

play03:59

introduced a long time ago so and that's

play04:04

called something called task so we can

play04:08

use tasks to run these functions

play04:11

asynchronously and the way to do it is

play04:14

to do tasks run and we can use slam for

play04:24

expression so we can run the first one

play04:34

and then we run the second one we're

play04:37

doing this third one second one third

play04:45

one

play04:49

and let's give it a try

play04:57

all right so this time the result looks

play05:00

different

play05:01

calculating result to calculating

play05:03

results 3 in calculating result 1 y this

play05:08

time the order is different because the

play05:10

first time if you remember it says

play05:12

calculating resultant one and then

play05:15

result two and there is all three this

play05:18

time wide result one becomes the last

play05:21

one and then we look at the code the

play05:24

result one has a sleep statement to

play05:31

simulate that it's a long-running

play05:32

process whereas result two and three

play05:35

returns right away so that's why we see

play05:40

this happens secondly this is oppisite

play05:46

sec first firstly this is output

play05:49

secondly and finally this one was output

play05:53

so this tells us that at least the first

play06:00

one was running while the second one is

play06:03

there when a run was running so on the

play06:07

reason why this third one still comes

play06:10

secondly before after the second one was

play06:15

because calculating calculate 2 was

play06:20

started and then right away calculating

play06:25

three start so because calculation to

play06:29

takes like returns immediately so this

play06:33

one still finishes before this one but

play06:37

because calculate one takes three

play06:40

seconds to finish so that's why we're

play06:43

seeing that the output goes result to

play06:47

first root of three secondly and the

play06:50

result one right so let's is I'm in a

play06:54

different scenario that let's say

play06:57

calculation three depends on calculation

play07:01

one and two okay

play07:04

so let's put some parameter here

play07:06

resolved

play07:07

one result to and we're returning one

play07:17

plus result - okay

play07:22

and so in this case we cannot call it

play07:29

this way because otherwise first of all

play07:33

well first of all we cannot get the

play07:35

parameters we cannot get the return

play07:37

value from calculate one and calculate

play07:40

two secondly read all three will run

play07:45

before result one finishes so what

play07:50

should we do in this case okay so in in

play07:55

that case when you do something like

play07:57

this

play07:59

they return the tasks for the first two

play08:07

and here we need to return the value

play08:11

well yeah return the value and here we

play08:17

just say we want to weigh for both task

play08:20

one and task 2 to finish so there is a

play08:23

something called weight all so you can

play08:27

do weigh all or weight any and then we

play08:30

can say task 1 task to wait for all of

play08:35

them to finish and then we can get a

play08:43

waiter a waiter one because task 1 got a

play08:50

waiter and a waiter to the coach task to

play08:59

get a waiter and from my waiter we can

play09:02

get the result so we're gonna get a

play09:05

result result 1 equals waiter 1 the

play09:12

result and result two eCos a waiter to

play09:20

get result so in this case we are able

play09:28

to get the result from one or two from

play09:31

calculate one and calculate two and then

play09:34

we can feed it to calculate three

play09:37

calculate three ways result one the

play09:41

result

play09:42

- okay let's front-end see this time the

play09:53

sequence is different again because

play09:58

because calculate one runs first but it

play10:05

takes longer for it to finish so that's

play10:08

why I calculate to print to the screen

play10:09

first

play10:10

Cokely to print to the screen first and

play10:13

then calculate one and because calculate

play10:16

three weights to both of the to finish

play10:19

that's why I calculate three appears on

play10:22

the screen on the third place alright so

play10:28

let's take let's see a different

play10:32

scenario where maybe calculate two

play10:37

depends on calculate one right

play10:45

so calculate two depends on calculate

play10:47

one in that case we want to change this

play10:50

to result one and then they want to do

play11:05

result one times two we return this and

play11:11

so that's this makes result two relies

play11:16

on result one by result three does not

play11:19

rely on result one so so what we need to

play11:24

do in this case

play11:26

is that we need to maybe we can use this

play11:32

wanna got the waiter one right we got a

play11:39

waiter one and then we can say a waiter

play11:44

dot uncompleted okay so we were waiting

play11:50

for the task one to complete and then

play11:56

inside here we can get results from my

play12:02

waiter one we can get so here

play12:13

a waiter one when a waiter one completes

play12:16

which means when the task first first

play12:19

task complete

play12:21

then we use a waiter one to get the

play12:23

result this is the result from calculate

play12:26

one which is an integer and then we can

play12:31

call calculate two and feed the result

play12:37

one to calculate two and after that

play12:43

we'll call calculate three we can make

play12:46

calculate three has no relationship with

play12:49

chocolate to test us not depend it does

play12:53

not depend on calculate one or coke

play12:55

later here we just return 300 all right

play13:02

in this case we look at again task one

play13:07

runs but then when it finishes it costs

play13:11

this so this is so-called callback

play13:14

function this is a callback callback

play13:16

function so them their expression it's a

play13:19

delegate and this is the function this

play13:24

here is the function and what it does is

play13:27

that it gets the result from task one

play13:30

and then cause chocolate chocolate - and

play13:34

this is a async there's call so

play13:37

calculates three will

play13:39

actually we'll run first because it

play13:41

takes a long time takes three seconds to

play13:43

for task 1 to finish so what we'll see

play13:48

is that it was a calculating resolved 3

play13:52

and then it will print calculating

play13:55

result 1 and call anything result - so

play13:58

let's see whether it does that or not so

play14:01

calculating grizzle 3 that comes first

play14:04

and then 1 and 2 so so this is this is

play14:12

the async async there's programming

play14:15

functionality in c-sharp there's a lot

play14:17

of different other things but this is

play14:19

the basic of the of the asynchronous

play14:25

programming in C sharp and it's using a

play14:30

sort of callback and if we want to wait

play14:33

for something we can use we can use

play14:36

tasks to wait who either wait anyway oh

play14:41

so there's another thing that we now

play14:44

they see a lot of function test that

play14:47

says async at the first and then inside

play14:52

it says await

play14:53

so what is about those what is about the

play14:57

the async and the way it keywords it

play14:59

looks sort of confusing because looks

play15:01

like it's not a asynchronous programming

play15:03

it looks like a sink neurs like

play15:05

everything is as being called

play15:07

synchronously and that's actually the

play15:09

point the point is that to simplify the

play15:14

async there's programming so that it has

play15:18

a feel of sink there's programming but

play15:20

but it's a table Osiris sword it's it's

play15:24

it is actually indeed confusing so so

play15:27

this this way we do it this way they're

play15:31

actually pretty clear that this is a

play15:34

this is a callback function and this

play15:37

runs first I mean this this these will

play15:41

run first but then it will go through

play15:43

and then this one this will actually

play15:46

goes through really quickly and then we

play15:48

run this one and then when when all of

play15:50

that finishes because in

play15:51

this structure is pretty clear but but

play15:57

but it's probably nicer if we can we can

play16:00

make it look like a sink nearest

play16:02

programming so let's see let's see how

play16:06

we I say in this same case that

play16:11

calculate - depends on calculate one and

play16:14

calculates three does not in this case

play16:16

what do we do alright so what do we do

play16:19

how do we how do we use a sink and a way

play16:25

to implement the same thing so so for

play16:29

that we want to create a new a new

play16:36

function I'll call it task I'll

play16:42

calculate one and two and inside here we

play16:47

are going to have we're gonna call this

play16:52

copy this over here right and we are

play16:59

going to await this and when we are

play17:03

awaited we will actually get a result

play17:06

from here right and this result is an

play17:11

integer and here we are because we're

play17:16

waiting for that for calculate want to

play17:19

finish then at this line we will get the

play17:22

result

play17:23

already all right we'll get it so that

play17:28

we can call calculate two feet add to it

play17:42

so in this case

play17:51

we can call okay one two yep this

play18:13

supposed to be static so we're calling

play18:17

this function and because we are not

play18:26

awaiting it we're not waiting it to

play18:27

finish so as soon as this executes well

play18:32

this calculate 3 will be executed right

play18:36

and then let's run it and let's see how

play18:42

it works so it does the calculation 3

play18:50

first and then first and then does the

play18:55

calculating one and calculating two and

play19:00

let's see how it works so this function

play19:04

gets called first and what happens here

play19:08

is that it runs calculate 1 and actually

play19:14

it will actually return from here and

play19:17

then directly calculates this and after

play19:21

does calculate 1 finishes it will come

play19:24

back and execute calculate 2

play19:31

hopefully that's clear so so so again

play19:35

calculates fun calculate is called and

play19:38

calculate 1 2 function which is this

play19:40

function is called when this function is

play19:42

called this task gets executed but

play19:47

because we have a weight keyword here

play19:50

this async and the way to mix this

play19:52

calculate one and a score to function a

play19:56

asynchronous function so will actually

play19:59

return right away

play20:00

kind of like finishes but kind of like a

play20:02

pauses right away

play20:04

then if I can execute the next lines and

play20:07

next line the next line as soon as the

play20:11

stocks finishes I will continue from

play20:16

here

play20:17

I remember a past so it will continue

play20:21

from where it paused so continue from

play20:24

here that's why we saw calculating 3

play20:28

first and then calculating one and then

play20:30

calculating - it didn't show calculating

play20:34

- right it showed calculating one before

play20:38

showing calculating - so calculating -

play20:41

is actually waiting for calculating one

play20:45

to finish but calculating three runs

play20:49

first because this is a Sinclairs

play20:53

function what if we add a what if we add

play21:01

a here they think and then we and we

play21:12

make this return a task and we await we

play21:17

await this one a way to calculate one

play21:22

and two and then we call it we also we

play21:31

can await this one so yeah so what if we

play21:39

create another static method and call it

play21:44

test from here we make this one an async

play21:51

and we await a way to calculate or we

play22:00

calculate

play22:05

well we have to or we got a task here

play22:09

and we do this what will happen guess

play22:14

what will happen I'm gonna put a test

play22:21

here of course so the task will be

play22:24

called and then it will await this to

play22:28

finish and when this is called

play22:35

will await 1 & 2 to finish alright so

play22:40

cut calculus 3 will not be called it's

play22:42

that cochlear one and will be called and

play22:46

when one finishes so actually pauses

play22:51

here right but but then when it finishes

play22:54

calls calculate 2 and when calculate 1 &

play22:59

2 finishes the cost chemistry so when we

play23:05

run in or see that calculate 1 prints

play23:08

first and then 2 & 3 so apart by the

play23:11

normal sequence see 1 2 & 3

play23:26

yeah I think that's all I want to talk

play23:28

about today about anything there's

play23:31

programming and I hope that is helpful

play23:37

if you liked it please give it a thumb

play23:40

thumb up and subscribe as well thank you

play23:43

so much

Rate This

5.0 / 5 (0 votes)

Etiquetas Relacionadas
Async ProgrammingC# TutorialCode ExamplesSynchronous vs AsyncTask ManagementCoding ConceptsSoftware DevelopmentLife AnalogyProgramming TipsC# Async
¿Necesitas un resumen en inglés?