C # from breakfast to synchronous asynchronous

summary

The plan of the day is in the morning, and breakfast is also essential every day. However, in order to save time, many people simply eat something to make do or don't eat breakfast at all, which is undoubtedly unreasonable for personal body and work efficiency. So how to make a breakfast? How can you save time for breakfast? With a simple example, this paper briefly describes how to make a breakfast and how to optimize the time of making breakfast. Only for learning and sharing. Please correct any deficiencies.

Under normal circumstances, making breakfast can be divided into the following steps:

  1. Pour a cup of coffee.
  2. Heat the pan and fry two eggs.
  3. Fry three slices of bacon.
  4. Bake two slices of bread.
  5. Add butter and jam to the toast.
  6. Pour a glass of orange juice.

Synchronous breakfast

Program according to the above steps. To make a breakfast, you need to write the following program:

  1         /// <summary>
  2         /// Make breakfast at the same time
  3         /// </summary>
  4         /// <param name="sender"></param>
  5         /// <param name="e"></param>
  6         private void btnBreakfast_Click(object sender, EventArgs e)
  7         {
  8             this.txtInfo.Clear();
  9             Stopwatch watch = Stopwatch.StartNew();
 10             watch.Start();
 11             //1. Pour a cup of coffee.
 12             string cup = PourCoffee();
 13             PrintInfo("The coffee is ready");
 14             //2. Heat the pan and fry two eggs. 
 15             string eggs = FryEggs(2);
 16             PrintInfo("The eggs are fried");
 17             //3. Fry three slices of bacon. 
 18             string bacon = FryBacon(3);
 19             PrintInfo("The bacon is fried");
 20             //4. Bake two slices of bread. 
 21             string toast = ToastBread(2);
 22             //5. Add butter and jam to the toast.
 23             ApplyButter(toast);
 24             ApplyJam(toast);
 25             PrintInfo("The bread is ready");
 26             //6. Pour a glass of orange juice.
 27             string oj = PourOJ();
 28             PrintInfo("The orange juice is ready");
 29             PrintInfo("Breakfast is ready!");
 30             watch.Stop();
 31             TimeSpan time = watch.Elapsed;
 32             PrintInfo(string.Format("The total running time is:{0}second", time.TotalSeconds.ToString("0.00")));
 33         }
 34 
 35         /// <summary>
 36         /// Pour a cup of coffee
 37         /// </summary>
 38         /// <returns></returns>
 39         private string PourCoffee()
 40         {
 41             PrintInfo("Making coffee...");
 42             return "Coffee";
 43         }
 44 
 45         /// <summary>
 46         /// Spread jam
 47         /// </summary>
 48         /// <param name="toast"></param>
 49         private void ApplyJam(string toast) =>
 50             PrintInfo("Spread jam on the bread");
 51 
 52         /// <summary>
 53         /// Butter
 54         /// </summary>
 55         /// <param name="toast"></param>
 56         private void ApplyButter(string toast) =>
 57             PrintInfo("Butter the bread");
 58 
 59         /// <summary>
 60         /// to toast bread
 61         /// </summary>
 62         /// <param name="slices"></param>
 63         /// <returns></returns>
 64         private string ToastBread(int slices)
 65         {
 66             for (int slice = 0; slice < slices; slice++)
 67             {
 68                 PrintInfo("Put bread in the oven");
 69             }
 70             PrintInfo("Start baking...");
 71             Task.Delay(3000).Wait();
 72             PrintInfo("Remove the bread from the oven");
 73 
 74             return "to toast bread";
 75         }
 76 
 77         /// <summary>
 78         /// Bacon 
 79         /// </summary>
 80         /// <param name="slices"></param>
 81         /// <returns></returns>
 82         private string FryBacon(int slices)
 83         {
 84             PrintInfo($"discharge {slices} Slice bacon in a pan");
 85             PrintInfo("Fry the first slice of bacon...");
 86             Task.Delay(3000).Wait();
 87             for (int slice = 0; slice < slices; slice++)
 88             {
 89                 PrintInfo("Flip Bacon");
 90             }
 91             PrintInfo("Fry the second slice of bacon...");
 92             Task.Delay(3000).Wait();
 93             PrintInfo("Put the bacon on the plate");
 94 
 95             return "Bacon ";
 96         }
 97 
 98         /// <summary>
 99         /// Fried egg
100         /// </summary>
101         /// <param name="howMany"></param>
102         /// <returns></returns>
103         private string FryEggs(int howMany)
104         {
105             PrintInfo("Heated pan...");
106             Task.Delay(3000).Wait();
107             PrintInfo($"Knock open {howMany} An egg");
108             PrintInfo("Fried egg ...");
109             Task.Delay(3000).Wait();
110             PrintInfo("Put the eggs on the plate");
111 
112             return "Fried egg";
113         }
114 
115         /// <summary>
116         /// Pour orange juice
117         /// </summary>
118         /// <returns></returns>
119         private string PourOJ()
120         {
121             PrintInfo("Pour a glass of orange juice");
122             return "Orange Juice";
123         }

Synchronous breakfast example

Through the running example, it is found that the synchronous programming method is adopted to make a breakfast for 15 seconds, and within this 15 seconds, the program is in the [stuck] state and cannot carry out other operations. As follows:

Schematic diagram of synchronous breakfast making

Breakfast is made in synchronization mode, that is, one is finished, and then the next is executed in sequence, as shown below:

Why is the synchronization mode [stuck]?

In the process of the program, there will be a main thread to respond to the user's operation. In the synchronous mode, the breakfast making page and the front page are in the main thread, so when you start making breakfast, you can't respond to other operations. This is the realm of "two ears don't hear things outside the window, and one mind only reads the books of sages". However, if you let users wait for a long time, it will make the user experience very unfriendly. For example, Liu Xuande looked at the thatched cottage three times. Under the heavy snow, Zhuge Liang slept in the thatched cottage at noon, Liu Guanzhang was quiet in the heavy snow, etc. How many people will have Xuande's patience? Besides, the program is not Zhuge Liang, and users don't have Xuande's patience!

Asynchronous breakfast

The above code demonstrates the incorrect practice of constructing synchronous code to perform asynchronous operations. As the name implies, this code will prevent the thread executing this code from performing any other operations. This code will not be interrupted during any task. Just like you put bread in the toaster and stare at the toaster. You will ignore anyone who speaks to you until the bread pops up. What can I do to avoid thread blocking? The answer is asynchronous.   await   Keyword provides a non blocking way to start a task and then continue execution when the task is completed.

First, update the code. For time-consuming programs, breakfast is made asynchronously, as shown below:

 1         private async void btnBreakfastAsync_Click(object sender, EventArgs e)
 2         {
 3             this.txtInfo.Clear();
 4             Stopwatch watch = Stopwatch.StartNew();
 5             watch.Start();
 6             //1. Pour a cup of coffee.
 7             string cup = PourCoffee();
 8             PrintInfo("The coffee is ready");
 9             //2. Heat the pan and fry two eggs. 
10             //Task<string> eggs = FryEggsAsync(2);
11             string eggs =await FryEggsAsync(2);
12             PrintInfo("The eggs are fried");
13             //3. Fry three slices of bacon. 
14             string bacon =await FryBaconAsync(3);
15             PrintInfo("The bacon is fried");
16             //4. Bake two slices of bread. 
17             string toast =await ToastBreadAsync(2);
18             //5. Add butter and jam to the toast.
19             ApplyButter(toast);
20             ApplyJam(toast);
21             PrintInfo("The bread is ready");
22             //6. Pour a glass of orange juice.
23             string oj = PourOJ();
24             PrintInfo("The orange juice is ready");
25             PrintInfo("Breakfast is ready!");
26             watch.Stop();
27             TimeSpan time = watch.Elapsed;
28             PrintInfo(string.Format("The total running time is:{0}second", time.TotalSeconds.ToString("0.00")));
29         }
30 
31         /// <summary>
32         /// Asynchronous toast
33         /// </summary>
34         /// <param name="slices"></param>
35         /// <returns></returns>
36         private async Task<string> ToastBreadAsync(int slices)
37         {
38             for (int slice = 0; slice < slices; slice++)
39             {
40                 PrintInfo("Put bread in the oven");
41             }
42             PrintInfo("Start baking...");
43             await Task.Delay(3000);
44             PrintInfo("Remove the bread from the oven");
45 
46             return "to toast bread";
47         }
48 
49         /// <summary>
50         /// Fried bacon
51         /// </summary>
52         /// <param name="slices"></param>
53         /// <returns></returns>
54         private async Task<string> FryBaconAsync(int slices)
55         {
56             PrintInfo($"discharge {slices} Slice bacon in a pan");
57             PrintInfo("Fry the first slice of bacon...");
58             await Task.Delay(3000);
59             for (int slice = 0; slice < slices; slice++)
60             {
61                 PrintInfo("Flip Bacon");
62             }
63             PrintInfo("Fry the second slice of bacon...");
64             await Task.Delay(3000);
65             PrintInfo("Put the bacon on the plate");
66 
67             return "Bacon ";
68         }
69 
70         /// <summary>
71         /// Asynchronous fried egg
72         /// </summary>
73         /// <param name="howMany"></param>
74         /// <returns></returns>
75         private async Task<string> FryEggsAsync(int howMany)
76         {
77             PrintInfo("Heated pan...");
78             await Task.Delay(3000);
79             PrintInfo($"Knock open {howMany} An egg");
80             PrintInfo("Fried egg ...");
81             await Task.Delay(3000);
82             PrintInfo("Put the eggs on the plate");
83 
84             return "Fried egg";
85         }

  Note: through the test, it is found that the execution time of asynchronous mode and synchronous mode is the same, so the asynchronous mode will not shorten the time, but the program is no longer blocked and can respond to other user requests at the same time.

Optimize asynchronous breakfast

Through the above asynchronous methods, although the program is optimized and no longer blocked, the time is not shortened. So how to optimize the program to shorten the time so as to have a delicious breakfast early? The answer is that after starting a task, you can continue to prepare for other tasks while waiting for the task to be completed.   You will also finish almost all your work at the same time. You'll have a steaming breakfast. By merging tasks and adjusting the order of tasks, the completion time of tasks will be greatly saved, as shown below:

 1         /// <summary>
 2         /// Optimize asynchronous breakfast
 3         /// </summary>
 4         /// <param name="sender"></param>
 5         /// <param name="e"></param>
 6         private async void btnBreakfast2_Click(object sender, EventArgs e)
 7         {
 8             this.txtInfo.Clear();
 9             Stopwatch watch = Stopwatch.StartNew();
10             watch.Start();
11             //1. Pour a cup of coffee.
12             string cup = PourCoffee();
13             PrintInfo("The coffee is ready");
14             //2. Heat the pan and fry two eggs. 
15             Task<string> eggsTask = FryEggsAsync(2);
16             //3. Fry three slices of bacon. 
17             Task<string> baconTask = FryBaconAsync(3);
18             //4.5 Together, toast, spread jam and butter
19             Task<string> toastTask = MakeToastWithButterAndJamAsync(2);
20 
21             string eggs = await eggsTask;
22             PrintInfo("The eggs are fried");
23 
24             string bacon = await baconTask;
25             PrintInfo("The bacon is fried");
26 
27             string toast = await toastTask;
28             PrintInfo("The bread is ready");
29             //6. Pour a glass of orange juice.
30             string oj = PourOJ();
31             PrintInfo("The orange juice is ready");
32             PrintInfo("Breakfast is ready!");
33             watch.Stop();
34             TimeSpan time = watch.Elapsed;
35             PrintInfo(string.Format("The total running time is:{0}second", time.TotalSeconds.ToString("0.00")));
36         }
37 
38         /// <summary>
39         /// Combined task
40         /// </summary>
41         /// <param name="number"></param>
42         /// <returns></returns>
43         private async Task<string> MakeToastWithButterAndJamAsync(int number)
44         {
45             var toast = await ToastBreadAsync(number);
46             ApplyButter(toast);
47             ApplyJam(toast);
48             return toast;
49         }

In this example, bread baking + jam smearing + butter smearing are combined as one task, so that eggs and bacon can be fried while baking bread, and the three time-consuming tasks can be executed at the same time. After all three tasks are completed, breakfast is ready. The example is as follows:

Through the above optimization example, it is found that it takes 6.06 seconds to make a breakfast by merging tasks and adjusting the order.

Schematic diagram of optimizing asynchronous breakfast

The optimized asynchronous breakfast saves time because some tasks run concurrently. The schematic diagram is as follows:

Asynchronous exception

The above example assumes that all tasks can be completed normally. How to catch exceptions in the execution of a task? The answer is: when tasks fail to complete successfully, they throw exceptions. When the task started is   awaited   Client code can catch these exceptions when.

For example, when baking bread, the oven suddenly caught fire. How to deal with exceptions? The code is as follows:

 1         private async void btnBreakfastAsync3_Click(object sender, EventArgs e)
 2         {
 3             try
 4             {
 5                 this.txtInfo.Clear();
 6                 Stopwatch watch = Stopwatch.StartNew();
 7                 watch.Start();
 8                 //1. Pour a cup of coffee.
 9                 string cup = PourCoffee();
10                 PrintInfo("The coffee is ready");
11                 //2. Heat the pan and fry two eggs. 
12                 Task<string> eggsTask = FryEggsAsync(2);
13                 //3. Fry three slices of bacon. 
14                 Task<string> baconTask = FryBaconAsync(3);
15                 //4.5 Together, toast, spread jam and butter
16                 Task<string> toastTask = MakeToastWithButterAndJamAsyncEx(2);
17 
18                 string eggs = await eggsTask;
19                 PrintInfo("The eggs are fried");
20 
21                 string bacon = await baconTask;
22                 PrintInfo("The bacon is fried");
23 
24                 string toast = await toastTask;
25                 PrintInfo("The bread is ready");
26                 //6. Pour a glass of orange juice.
27                 string oj = PourOJ();
28                 PrintInfo("The orange juice is ready");
29                 PrintInfo("Breakfast is ready!");
30                 watch.Stop();
31                 TimeSpan time = watch.Elapsed;
32                 PrintInfo(string.Format("The total running time is:{0}second", time.TotalSeconds.ToString("0.00")));
33             }
34             catch (AggregateException ex) {
35                 PrintInfo("Thread internal exception");
36                 PrintInfo(ex.StackTrace);
37             }
38             catch (Exception ex)
39             {
40                 PrintInfo("Other exceptions");
41                 PrintInfo(ex.Message);
42             }
43         }
44 
45         /// <summary>
46         /// Combined task
47         /// </summary>
48         /// <param name="number"></param>
49         /// <returns></returns>
50         private async Task<string> MakeToastWithButterAndJamAsyncEx(int number)
51         {
52             var toast = await ToastBreadAsyncEx(number);
53             ApplyButter(toast);
54             ApplyJam(toast);
55             return toast;
56         }
57 
58         /// <summary>
59         /// Asynchronous toast exception
60         /// </summary>
61         /// <param name="slices"></param>
62         /// <returns></returns>
63         private async Task<string> ToastBreadAsyncEx(int slices)
64         {
65             for (int slice = 0; slice < slices; slice++)
66             {
67                 PrintInfo("Put bread in the oven");
68             }
69             PrintInfo("Start baking...");
70             await Task.Delay(2000);
71             PrintInfo("It's on fire! The bread is burnt!");
72             int a = 1, b = 0;
73             int i = a / b;//Create an exception
74             //throw new InvalidOperationException("The oven is on fire!");
75             await Task.Delay(1000);
76             PrintInfo("Remove the bread from the oven");
77 
78             return "to toast bread";
79         }

Asynchronous task exception example

Please note that there are quite a lot of tasks to be completed from the fire of the toaster to the discovery of abnormalities. An error occurs when an asynchronously running task throws an exception. The task object contains   Task.Exception   Property. The faulted task threw an exception while waiting.

Two important mechanisms need to be understood: how exceptions are stored in the wrong task, and how exceptions are unpacked and re thrown when code waits for the wrong task.

When code running asynchronously throws an exception, the exception is stored in the   Task   Yes.   Task.Exception   Attribute is   System.AggregateException because multiple exceptions may be thrown during asynchronous work. Any exceptions thrown are added to the   AggregateException.InnerExceptions   In the collection. If   Exception   Property is NULL, a new is created   AggregateException   And the exception thrown is the first item in the collection.

For tasks that go wrong, the most common situation is   Exception   Property contains only one exception. When code   awaits   A task with an error will be raised again   AggregateException.InnerExceptions   The first exception in the collection. Therefore, the output of this example shows   InvalidOperationException   instead of   AggregateException. Extracting the first inner exception makes using an asynchronous method as similar as using its corresponding synchronous method. When your scenario may generate multiple exceptions, you can check in the code   Exception   Properties.

Efficient waiting

Through the above example, you need to wait for many tasks to be completed before breakfast is ready. How can you wait efficiently and gracefully? You can use   Task   Class to improve the series at the end of the above code   await   sentence. One of the API s is   WhenAll, which will return a task that is completed only when all tasks in its parameter list have been completed   Task, as follows:

 1         private async void btnBreakfastAsync4_Click(object sender, EventArgs e)
 2         {
 3             this.txtInfo.Clear();
 4             Stopwatch watch = Stopwatch.StartNew();
 5             watch.Start();
 6             //1. Pour a cup of coffee.
 7             string cup = PourCoffee();
 8             PrintInfo("The coffee is ready");
 9             //2. Heat the pan and fry two eggs. 
10             Task<string> eggsTask = FryEggsAsync(2);
11             //3. Fry three slices of bacon. 
12             Task<string> baconTask = FryBaconAsync(3);
13             //4.5 Together, toast, spread jam and butter
14             Task<string> toastTask = MakeToastWithButterAndJamAsync(2);
15             //Wait for the task to complete
16             await Task.WhenAll(eggsTask, baconTask, toastTask);
17             
18             PrintInfo("The eggs are fried");
19             PrintInfo("The bacon is fried");
20             PrintInfo("The bread is ready");
21             //6. Pour a glass of orange juice.
22             string oj = PourOJ();
23             PrintInfo("The orange juice is ready");
24             PrintInfo("Breakfast is ready!");
25             watch.Stop();
26             TimeSpan time = watch.Elapsed;
27             PrintInfo(string.Format("The total running time is:{0}second", time.TotalSeconds.ToString("0.00")));
28         }

Another option is to use   WhenAny, which will return a value that is not completed until its parameters are completed   Task<Task>. As follows:

 1         private async void btnBreakfastAsync5_Click(object sender, EventArgs e)
 2         {
 3             this.txtInfo.Clear();
 4             Stopwatch watch = Stopwatch.StartNew();
 5             watch.Start();
 6             //1. Pour a cup of coffee.
 7             string cup = PourCoffee();
 8             PrintInfo("The coffee is ready");
 9             //2. Heat the pan and fry two eggs. 
10             Task<string> eggsTask = FryEggsAsync(2);
11             //3. Fry three slices of bacon. 
12             Task<string> baconTask = FryBaconAsync(3);
13             //4.5 Together, toast, spread jam and butter
14             Task<string> toastTask = MakeToastWithButterAndJamAsync(2);
15             //Wait for the task to complete
16             var breakfastTasks = new List<Task> { eggsTask, baconTask, toastTask };
17             while (breakfastTasks.Count > 0)
18             {
19                 Task finishedTask = await Task.WhenAny(breakfastTasks);
20                 if (finishedTask == eggsTask)
21                 {
22                     PrintInfo("The eggs are fried");
23                 }
24                 else if (finishedTask == baconTask)
25                 {
26                     PrintInfo("The bacon is fried");
27                 }
28                 else if (finishedTask == toastTask)
29                 {
30                     PrintInfo("The bread is ready");
31                 }
32                 breakfastTasks.Remove(finishedTask);
33             }
34             //6. Pour a glass of orange juice.
35             string oj = PourOJ();
36             PrintInfo("The orange juice is ready");
37             PrintInfo("Breakfast is ready!");
38             watch.Stop();
39             TimeSpan time = watch.Elapsed;
40             PrintInfo(string.Format("The total running time is:{0}second", time.TotalSeconds.ToString("0.00")));
41         }

The above is a step-by-step process from synchronous to asynchronous and then to optimize asynchronous tasks. It aims to throw a brick to attract jade, learn together and make progress together.

remarks

Yellow Crane Tower [author] Cui Hao   [Dynasty] Tang Dynasty

In the past, people have gone by yellow crane. There is no yellow crane tower here.

The yellow crane will never return, and the white clouds will be long in the sky for thousands of years.

Qingchuan has Hanyang trees, fragrant grass and parrot island.

Where is the sunset village? The Yanbo river is worrying.

Tags: C# async await

Posted on Sun, 05 Dec 2021 10:00:42 -0500 by callmecheez