Introduction
After discussing 17 topics, this article finally starts to learn about async and await. With the foundation laid in previous articles, understanding async and await becomes much easier.
In this section, it is crucial to write code for each example, execute it, and analyze the results and the thought process yourself.
async
According to Microsoft documentation: The async
modifier can be used to designate a method, lambda expression, or anonymous method as asynchronous.
Methods modified with async are called asynchronous methods.
For example:
For naming conventions, methods modified with async should have "Async" added at the end of their method names.
public async Task<int> TestAsync()
{
// . . . .
}
Lambda:
static void Main()
{
Thread thread = new Thread(async () =>
{
await Task.Delay(0);
});
}
public static async Task<int> TestAsync() => 666;
await
According to Microsoft documentation: The await
operator pauses the evaluation of the async method it belongs to until the asynchronous operation represented by its operand is complete.
After the asynchronous operation is complete, the await
operator returns the result of the operation (if any).
That’s it for now; this is all the information regarding async and await from the official documentation.
Deriving Knowledge from the Past
Here, you will learn with the author how to derive and understand how to use the two keywords async and await based on the knowledge gained from previous articles.
We will not refer to documents or books and will not look at examples from documents and books. We will advance step by step from synchronous and asynchronous tasks, slowly exploring how async and await provide convenience in asynchronous programming.
Creating Asynchronous Tasks
Scenario: It's the weekend, and I can play a game like Honor of Kings, but I haven't washed the clothes from yesterday. So, I use the washing machine to wash the clothes while playing a few rounds of Honor of Kings.
We can write a method as follows:
static void Main()
{
Console.WriteLine("准备洗衣服");
// Create a task for washing clothes
Task<int> task = new Task<int>(() =>
{
// Simulate washing time
int time = new Random().Next(2, 6);
Thread.Sleep(TimeSpan.FromSeconds(time));
return time;
});
Console.WriteLine("开始洗衣服");
// Let the washing machine wash the clothes
task.Start();
Console.WriteLine("我去打王者,让洗衣机洗衣服");
// Playing the game
Thread.Sleep(TimeSpan.FromSeconds(4));
Console.WriteLine("打完王者了,衣服洗完了嘛?");
Console.WriteLine(task.IsCompleted);
if (task.IsCompleted)
Console.WriteLine("洗衣服花的时间:" + task.Result);
else
{
Console.WriteLine("在等洗衣机洗完衣服");
task.Wait();
Console.WriteLine("洗衣服花的时间:" + task.Result);
}
Console.WriteLine("洗完了,捞出衣服,晒衣服,继续打王者去");
}
Creating Asynchronous Tasks and Returning Task
In the above example, even though the task is performed asynchronously, the code is all placed in Main, which makes it very hard to read and not acceptable as proper code. We will encapsulate the laundry task into a method and return Task.
In the Program class, we add the following method, which is used to execute the asynchronous task and return a Task object.
/// <summary>
/// Execute a task
/// </summary>
/// <returns></returns>
public static Task<int> TestAsync()
{
Task<int> task = new Task<int>(() =>
{
// Simulate washing time
int time = new Random().Next(2, 6);
Thread.Sleep(TimeSpan.FromSeconds(time));
return time;
});
task.Start();
return task;
}
In the Main method, change it to
static void Main()
{
Console.WriteLine("准备洗衣服");
// Create a task for washing clothes
Task<int> task = TestAsync();
// ... ...
}
However, the difference is still not significant.
Asynchronous Turning to Synchronous
We created an asynchronous method to execute a laundry task; after finishing the game, we need to check if the task is completed before proceeding, which brings in the concept of synchronization
. To maintain synchronization and obtain execution results, we used .Wait()
, .Result
.
Here, we try to convert the previous operation into synchronously and get the result.
class Program
{
static void Main()
{
int time = Test();
// ... ...
}
/// <summary>
/// Execute a task
/// </summary>
/// <returns></returns>
public static int Test()
{
Task<int> task = new Task<int>(() =>
{
// Simulate washing time
int time = new Random().Next(2, 6);
Thread.Sleep(TimeSpan.FromSeconds(time));
return time;
});
task.Start();
return task.Result;
}
}
Discussing await Task
Task
and Task<TResult>
, the former represents a task without a return result, while the latter is a task with a return result. Using await, we can achieve similar functionality with slightly modified syntax.
Task
:
public static void T1()
{
Task task = new Task(() => { });
task.Wait();
}
public static async void T2()
{
Task task = new Task(() => { });
await task;
}
This demonstrates that await can let the program wait for the task to complete.
Task<TResult>
:
public void T3()
{
// Obtain Task object, the following logic can be in asynchronous
Task<int> task = TestAsync();
// The task is executing asynchronously, we don’t concern it
// Here we can handle other tasks, and after they are done, retrieve the result
// This is asynchronous
Console.WriteLine(task.Result);
}
public async void T4()
{
// Using await means waiting for completion, hence synchronous
int time = await TestAsync();
Console.WriteLine(time);
}
Explanation: the await can make the program wait for the task to complete and obtain the result.
As you can see... the role of the await keyword is to let you wait; it is synchronous, and it does not directly transform your tasks into asynchronous background executions.
So why is async and await always referred to in relation to asynchronicity? Hold on, we will explain later.
Discussing async Task<TResult>
When the method is modified with async Task<TResult>
, it means that the method should return the result of await Task<TResult>
.
Comparison of two synchronous methods:
public static int Test()
{
Task<int> task = new Task<int>(() =>
{
// Simulate washing time
int time = new Random().Next(2, 6);
Thread.Sleep(TimeSpan.FromSeconds(time));
return time;
});
task.Start();
return task.Result;
}
public static async Task<int> TestAsync()
{
Task<int> task = new Task<int>(() =>
{
// Simulate washing time
int time = new Random().Next(2, 6);
Thread.Sleep(TimeSpan.FromSeconds(time));
return time;
});
task.Start();
int time = await task;
return time;
}
Synchronous vs Asynchronous?
Question: Isn’t async and await related to asynchronous methods? So why did the previous examples using await all became synchronous?
Question: Are the execution processes of methods using async and await synchronous or asynchronous?
Answer: It can be both synchronous and asynchronous; whether it's synchronous or asynchronous is in your hands.
- When you use await to call an asynchronous method, the execution process is synchronous.
- When you obtain the Task returned from an asynchronous method, it is asynchronous.
Recently, the author has received some questions from readers. Some of them used async and await to write business logic, thinking that it will improve performance; in fact, the results remain synchronous, with no performance improvement. Through the examples below, you will understand how to use it properly.
First, let’s write two methods to implement synchronous and asynchronous functionalities without using async and await keywords. The execution results of the two methods should be consistent.
/// <summary>
/// Synchronous
/// </summary>
/// <returns></returns>
public static int Test()
{
Task<int> task = new Task<int>(() =>
{
return 666;
});
task.Start();
return task.Result;
}
/// <summary>
/// Asynchronous
/// </summary>
/// <returns></returns>
public static Task<int> TestAsync()
{
Task<int> task = new Task<int>(() =>
{
return 666;
});
task.Start();
return task;
}
Can we merge the two methods? When we want synchronous, it's synchronous; when we want asynchronous, it's asynchronous; this way, we don’t need to write two methods!
Yes, we can! By using the async and await keywords, it can be easily achieved!
After merging, the code is as follows:
/// <summary>
/// Can be asynchronous or synchronous
/// </summary>
/// <returns></returns>
public static async Task<int> TestAsync()
{
Task<int> task = new Task<int>(() =>
{
return 666;
});
task.Start();
return await task;
}
How do we implement synchronization and asynchrony when calling this merged method?
Here are two examples from the author:
// await makes the task synchronous
public async void T1()
{
// Using await means waiting for completion, hence synchronous
int time = await TestAsync();
Console.WriteLine(time);
}
// Directly obtaining the returned Task achieves asynchrony
public void T2()
{
// Obtain the Task, the subsequent logic can be asynchronous
Task<int> task = TestAsync();
// The task is executing asynchronously, we don’t care about it
// Here we can handle other tasks, and after they are done, retrieve the result
// This is asynchronous
Console.WriteLine(task.Result);
}
By now, you should understand why using async and await still resulted in synchronous execution, right?
Task Wraps Asynchronous Task
Earlier, we used new Task()
to create tasks, while the Microsoft website often uses Task.Run()
to write examples using async and await.
Thus, we can modify the previous asynchronous task to:
/// <summary>
/// Can be asynchronous or synchronous
/// </summary>
/// <returns></returns>
public static async Task<int> TestAsync()
{
return await Task.Run<int>(() =>
{
return 666;
});
}
About Jumping to await for Asynchronous
When learning about asynchronicity in Baidu, authors sometimes indicate that after entering an asynchronous method, synchronous code is executed, and once await is encountered, it's asynchronous execution.
Of course, there are various interpretations.
With all the knowledge about tasks (Task) we've learned, this point is quite easy to explain.
Since async and await keywords are used, there will inevitably be a Task object present at the most basic level. The Task object is inherently asynchronous. When encountering await, it becomes asynchronous not because of await’s role but due to the foundation of having a Task.
Why Layers of await Appear
This applies to providers. When I need to provide an interface for you to use, the method remains asynchronous (async) due to the presence of the async and await keywords. I will continue to encapsulate, thus maintaining a multi-layer calling structure, as illustrated in the previous subsection's diagram.
.
However, when it comes to the caller, methods should not still be written using async and await; instead, they should be implemented synchronously or asynchronously based on the actual situation.
Through this article, have you understood async and await?
文章评论