asynchronous
Asynchronous means that one will execute this one and the other will be executed.
A little more terminology is that when a method is not fully executed, it surrenders control and lets other methods execute.
The opposite of asynchronous is called synchronous, which means waiting for the method to complete execution completely.
The meaning of asynchrony is "not waiting for complete execution", so that you can respond to something immediately.
For example a compression or decompression operation. There will be a pause, continue, and cancel button on the general software.
These three commands must be executed immediately, and cannot be executed until the compression is completely executed.
So here is an asynchronous operation, one will execute the compression task, and then check whether the pause has been clicked.
For another example, your CPU generally does not exceed 16 cores.
But open your task manager, there will be thousands of thread tasks in idle state.
A core can only be used as one thread (or two).
The way a core handles these thousands of threads can also be called asynchronous.
It takes a few milliseconds to execute this, and then a few milliseconds to execute that, making you think it is executed at the same time of.
In this process, one program is not completely executed, and another program is executed.
Asynchronous implementation
There are many different ways to implement asynchrony, and its core idea is to be able to execute other things before execution is complete.
The easiest way is to use delegation-based registration. Execute the delegate once the task is complete.
The place where the registered delegate is executed is indeed not the place where the delegate is executed, it is indeed asynchronous.
In Unity, iterator-based coroutines are used to achieve asynchrony.
Iterators yield return
can indeed implement other methods without fully executing the method .
So iterators can also be asynchronous.
The current c# uses an asynchronous mechanism based on Task
,, . One will do this, one will do that. Not in units of statements or methods. But as a unit. Direct constructs are usually passed in a delegate to contain the statement to be executed.async
await
Task
Task
async
Is a method modifier that Task
can be used in any returning method or anonymous delegate.
The modified method will automatically split into multiples according to the content Task
, and then Task
give you the last one as the return value.
await
Similar to yield return
, is the basis for segmentation. The asynchronous method will await
split into multiple according to the location Task
.
When the code executes here, it is possible to suspend the execution of this method.
async method
async
An asynchronous method is a method async
that is modified with a return value of void
, Task
, or Task<T>
type.
In an asynchronous method, the type you need to return is the Task<T>
type of T.
For example, if your return type is void
or Task
, this is a method with no return value.
The writing inside the method void
is the same as the return method, and you cannot return
follow it with a value.
If your return type is Task<int>
, then the inside of the method is written like the returned int
method,
you must return
follow it with a int
to end the method.
Unlike iterators, it does not await
decide whether to automatically synthesize based on whether you have written it.
As long as you use async
the modifier, it will be synthesized, regardless of whether you use it or not await
.
async Task<int> GetInt32Async(int i)
{
return i;
}
Task<int> GetInt32(int i)
{
return Task.FromResult(i);
}
Because of the characteristics of asynchronous methods and iterators, other methods may be executed before a method is fully executed,
so you cannot use reference parameters in the asynchronous method or iterator method synthesized by the compiler ref
, out
otherwise in
you will find: "I was clearly 1 just now, why is it now 2, and I didn't assign a value to him", a strange scene.
awaitable types
Like iterators, await
the syntax can be used as long as certain conditions are met.
These conditions also hint at the principle of async
how to synthesize Task
.
When a type has accessible no-argument GetAwaiter()
methods (including extension methods),
and the return type of this method has the following members
IsCompleted {get; }
:bool
A property of type indicating whether the asynchronous operation has completed.GetResult()
: This method returns the result of the asynchronous operation, which can be of any type includingvoid
OnCompleted(Action continuation)
: Pass in a delegate, indicating what to do after completion.
await
When something is encountered in an asynchronous method await
, it will suspend the current method like an iterator.
Console.WriteLine("准备调用异步方法");
var taskHello = HelloAsync();
Console.WriteLine("回到调用方法处了");
await taskHello;
async Task HelloAsync()
{
Console.WriteLine("异步方法开始了");
await Task.Yield();
Console.WriteLine("异步方法结束了");
}
准备调用异步方法
异步方法开始了
回到调用方法处了
异步方法结束了
If it is an ordinary method, then the method must be executed,
that is, the asynchronous method is output first , and then the output returns to the calling method .
return timing of await
The iterator is only continued by foreach
someone manually calling it . In the asynchronous method, when encountered, it will first judge whether this thing needs to wait. That is, IsCompleted in the above awaitable condition . That is to say, if you have waited before, or if it takes too long for you to execute other things, it has already been executed, so there is no need to wait any longer. In this way, the asynchronous method will not be suspended. For example swapping the above example would not pause.NextMove
await
Task.Yield()
Task.CompletedTask
If an asynchronous method is suspended, all subsequent content will be converted into delegates and passed to the OnCompleted method for registration.
So when waiting for completion, he can find a way to continue from there.
After the waiting is completed, the original thread will receive the notification, and when the thread leaves the method when the notification was received,
it will continue the previously suspended task.
Leaving refers to return
ending the current method or await
suspending the current method,
but does not include calling a new method in the method.
The example just now is because it is called in the main thread, and the program ends without the main thread.
If you put all these things in one method, you don't need it await
.
Start();
void Start()
{
Console.WriteLine("准备调用异步方法");
HelloAsync();
Console.WriteLine("回到调用方法处了");
}
Which thread executes depends on the environment configuration. If the configuration requirement is that the original thread executes,
then it will definitely wait for the original thread to end its method.
If the method is long, it can really get stuck on those async methods waiting to complete.
If multiple signals waiting for completion are received at this time, an asynchronous method will be randomly selected to continue.
If the configuration allows multi-threaded execution, it will be received immediately by the idle thread.
await with return value
If the return type of GetResultvoid
is not , then await
the return value can be deconstructed when used.
async Task HelloInt64Async()
{
Console.WriteLine("开始执行一个异步方法");
long l = await GetInt64Async(40);
Console.WriteLine($"获取到了异步返回值{
l}");
}
async Task<long> GetInt64Async(long l)
{
await Task.Yield();
return l;
}
By the time you're done asynchronously, of course you already have this return value.
prevent blocking
The calculation and interaction speed of CPU and memory is very fast.
Work that can be done with only the CPU and memory is called a CPU intensive task .
In turn, other tasks that require hard disk resources or network resources to interact are called IO-intensive tasks .
This kind of time is very long for the CPU, and it really does nothing.
And if it is executed in a synchronous blocking manner, it is necessary to start accessing the next web page when a web page is obtained.
Since I just need to wait, why not start all these tasks together and wait?
Task.Yield
It is the fastest asynchronous task, its role is really just to give up the execution right,
it can be said that once await
it will be completed immediately. Then we can make this wait longer.
For example Task.Delay(100)
, 100 milliseconds is a bit long for the CPU.
async Task HelloInt16Async()
{
Console.WriteLine("同时启动3个异步任务");
var t1 = GetInt16Async(6);
var t2 = GetInt16Async(18);
var t3 = GetInt16Async(60);
Console.WriteLine("开始等待着3个异步任务");
Console.WriteLine($"第1个任务等待完毕,他的值是{
await t1}");
Console.WriteLine($"第2个任务等待完毕,他的值是{
await t2}");
Console.WriteLine($"第3个任务等待完毕,他的值是{
await t3}");
}
async Task<short> GetInt16Async(short l)
{
await Task.Delay(100);
return l;
}
Calling an asynchronous method is creating a task, await
but waiting for this task to complete.
So if you want to create and wait separately, you need to save this Task
and only when needed await
.
async method scope
The entire method of an asynchronous method is the same scope.
If variables are declared inside using
, await
they will not be released until they are finished.
Some file read operations and network access operations are returned Task
.
But if you use using
declarations for them, you must complete the wait before returning the value.
If you directly use what you get from them Task
as the return value, then they will be released immediately, and
these asynchronous tasks cannot be completed.
Task<string> Get4399()
{
using HttpClient client = new HttpClient();
return client.GetStringAsync("www.4399.com");
//这个方法在刚刚访问4399的时候,网络类就会被释放。
}
async Task<string> Get4399Async()
{
using HttpClient client = new HttpClient();
return await client.GetStringAsync("www.4399.com");
}
exception handling
If you need to use it in an asynchronous method try-catch
, you only need to surround it like a normal method.
Values obtained through await
parsing have the same status as ordinary variables and do not require special handling.
If there is an error in the waiting thing, the GetResult method will throw this exception when it is called (that is, await
parsed) .
On the contrary, the previous asynchrony based on registered delegate events will constitute a callback hell.
Each registered delegate is in a different scope and needs to be try-catch
surrounded by a lot of writing.
Here is just a demonstration of the nesting hell caused by the previous version of the event callback.
void GetHtml(string url, Action<string> callback)
{
// 模拟从网页获取源码
Console.WriteLine("获取网页源码...");
var client = new WebClient();
client.DownloadStringCompleted += (sender, e) =>
{
if (e.Error == null)
{
var html = e.Result;
callback(html);
}
else
{
throw e.Error;
}
};
client.DownloadStringAsync(new Uri(url));
}
void WriteFile(string html, Action<bool> callback)
{
// 模拟将源码写入到磁盘
Console.WriteLine("写入到磁盘...");
var path = "html.txt";
using (var stream = new FileStream(path, FileMode.Create))
{
var bytes = Encoding.UTF8.GetBytes(html);
stream.BeginWrite(bytes, 0, bytes.Length, ar =>
{
try
{
stream.EndWrite(ar);
callback(true);
}
catch (Exception ex)
{
throw ex;
}
}, null);
}
}
void Process()
{
try
{
GetHtml("www.4399.com", html =>
{
try
{
WriteFile(html, success =>
{
try
{
Console.WriteLine("完成");
}
catch (Exception ex)
{
Console.WriteLine("发生错误:" + ex.Message);
}
});
}
catch (Exception ex)
{
Console.WriteLine("发生错误:" + ex.Message);
}
});
}
catch (Exception ex)
{
Console.WriteLine("发生错误:" + ex.Message);
}
}
Manual splicing Task
Async usually uses await and uses in async methods await
.
But in some cases, it cannot be used await
, for example, the accessor of the property cannot be async
modified.
Or, override the return Task
method of the base class, but do not need to use it await
.
async
It is a decoration that indicates that the compiler needs to be synthesized before each method, and does not belong to the method signature.
fake task
Task.CompletedTask
will give you a completed taskTask.FromResult
method or create a completed task with the completion value you giveTask.FromCanceled
method creates a canceled task from the specified cancellation token.Task.FromException
method creates a task that throws an exception from the specified exception.
This also corresponds to one Task
of the four end situations. CompletedTask
and FromResult
indicate the successful completion of the task,
respectively, no return value and return value Task
.
FromCanceled
and FromException
represent canceled and abnormally terminated failed tasks, respectively.
A complete method should have a return value. An asynchronous method is not yet executed, and its return value will be Task
saved.
Then if you want a method that should have a return value to have no return value, abnormal interruption is the only way.
Actually cancellation also throws an exception .
pure waiting task
Task.Delay
The method can accept a number representing milliseconds, or a regular event interval class TimeSpan
.
This method will create a task after the specified time is completed, and the only content is to wait for the timer to respond.
However, task scheduling itself has a time error, and there may be a wait of up to 20 milliseconds after the time expires.
If the specified time is 0 , then it is completed immediately and will not be await
suspended.
Task.Yield()
The semantics of is to yield the current execution right only. Similar to waiting, but does not specify a specific time,
but is set to complete at the same time when you visit him. So in theory it can be treated as waiting for a fairly small amount of time.
But in fact, the error of task scheduling still has an impact, so Task.Delay(1)
it is no different from using it directly.
construction task
You can manually construct one by passing in a delegate to the constructor Task
.
But tasks created in this way need to be activated manually.
Task taskCw = new Task(() => {
Console.WriteLine("hello"); });
taskCw.Start();
Or use his static method Task.Run
to construct and start a task directly.
But this task runs on the thread pool, and some places cannot access data across threads.
var taskCw2 = Task.Run(() => {
Console.WriteLine("hello"); });
stitching task
continuation task
Task
The method of the instance ContinueWith
creates a task that will not start executing until the target has finished executing.
using HttpClient client = new HttpClient();
var httpTask = client.GetStringAsync("http://www.4399.com");
var fileTask = httpTask.ContinueWith(http =>
{
File.WriteAllText(@"D:\4399.html", http.Result);
});
fileTask
It's httpTask
a continuation of the mission. He decides what to do when the goal is accomplished.
The commission he uses has a parameter, which is the completed task Task
.
So when using this return value in the delegate, Task
you need to access its return value yourself.
If no configuration is carried, the continuation task will not be executed by default. A continuation task is automatically activated when the source task completes successfully.
But if the source task is canceled or exceptiond, then the continuation task is immediately set to the same cancellation or exception and will not execute.
timing task
Task.WhenAll
Multiple parameters can be received Task
, and this task will be completed when all parameters are completed.
He didn't actually perform the operation himself, he just waited.
If all the parameters are of the same type Task<T>
, then the return value Task<T[]>
is
to put all the parameters into an array as the completion value. If any one parameter cancels or exceptions,
then the whole task is canceled or exceptions.
Task.WhenAny
Multiple parameters can be received Task
, and when any parameter is completed, the task will be completed.
And his return value is that Task<Task<T>>
the inner layer Task
is the entire data that completed the first task.
async Task SelectHero(int player)
{
await Task.Delay(Random.Shared.Next(100, 2000));
Console.WriteLine($"玩家{
player}选择英雄完毕");
}
async Task Countdown(int seconds)
{
await Task.Delay(seconds);
Console.WriteLine($"倒计时结束,为没有选择英雄的玩家随机选择英雄");
}
Task[] selectTasks = new Task[5];
for (int i = 0; i < 5; i++)
{
selectTasks[i] = SelectHero(i + 1);
}
//所有玩家都选择完毕英雄
var allSelectHero=Task.WhenAll(selectTasks);
//倒计时
Task countdown = Countdown(1600);
//选择英雄或倒计时任何一个完成
Task startGame = Task.WhenAny(allSelectHero,countdown);
await startGame;
Console.WriteLine("游戏开始");
synchronous blocking wait
After getting one Task
, you can call its Wait
method to wait synchronously and block.
In this case, the execution of the method will not be switched, but it will wait for the task to complete.
For those with a return value Task
, you can also use Result
to get his return value.
This property will also be called first Wait
, and will wait for it to complete.
And if an exception or cancellation occurs during the period, it will be thrown when calling Wait
or Result
waiting.
So if there may be exceptions, you need to put Wait
or Result
in try
.
var taskResult=Task.FromResult(12);
taskResult.Wait();
int Result=taskResult.Result;
For methods of waiting for multiple tasks together, there are also Task.WaitAll
and Task.WaitAny
.
They are used in the same way as Task.WhenAll
, Task.WhenAny
except that instead of returning Task
, they wait.
task completion
Among the properties of a task instance, there are the following 4 bool
properties
IsCanceled
: task was canceledIsFaulted
: An exception occurred in the taskIsCompletedSuccessfully
: run successfully to the endIsCompleted
: The task has finished running, regardless of success, cancellation, or exception.
In addition to these final state properties, there is another Status
property that returns an enumeration. This enum has the following states
- Created : The initial state when using the constructor to create the task, you need to call the Start method to start it.
- WaitingForActivation : The initial state when using the
Task.Run
orTaskFactory.StartNew
method to create a task, .Net has a slight delay in its creation and scheduling into the task scheduler. - WaitingToRun : Waiting in the task scheduler to be assigned to a thread for execution.
- Running : The state when the commission started to execute, running, but not yet completed.
- RanToCompletion : The final state when execution completes without exceptions or cancellations.
- Canceled : The final state when canceled, either before or during execution.
- Faulted : The final state when an exception was thrown.
- WaitingForChildrenToComplete : The state when a child task is created using the TaskCreationOptions.AttachedToParent option, has completed execution, and is waiting for the child task to complete.
Cancel
The cancellation operation is related to two classes, the cancellation token source CancellationTokenSource
and the cancellation token CancellationToken
.
The receiver will get a cancellation token, which can only passively accept cancellation information.
Cancellation tokens are all generated by the source of the cancellation token, but the source of the cancellation token has a cancel method, so this cannot be given to the receiver.
An asynchronous method usually Async
ends with and has an optional parameter that accepts a cancellation token.
Cancellation tokens are value types, so using them directly default
yields a default value that you can use.
If it is a reference type, it needs to be judged as non-null.
cancel token source
Cancellation Token Source is a class that is responsible for generating and managing cancellation tokens. It has the following main members:
Token
Property: Returns a cancellation token associated with this source, which can be passed to asynchronous methods that need to support cancellation.Cancel
Method: Notifies this source and all associated cancellation tokens that a cancellation operation has been requested. This causes all registered callback functions to be executed, and all asynchronous methods observing the token to throwOperationCanceledException
exceptions.CancelAfter
Method: After a specified time interval,Cancel
the method is called automatically. This can be used to implement timeout functionality.CreateLinkedTokenSource
Static method: Creates a new source of cancellation tokens that is chained with the specified one or more other sources. This means that when any one of the linked sources is cancelled, the newly created source is also cancelled.
cancel token
A cancellation token is a structure that indicates whether cancellation of an operation has been requested. It has the following main members:
IsCancellationRequested
Property: Returns a boolean indicating whether cancellation of the operation has been requested. If true, the operation has been requested to be cancelled, otherwise, false.CanBeCanceled
Attributes: Returns a boolean indicating whether the token can be revoked. The cancellation token stores the source of the cancellation token generated by itself, and the field of the cancellation token created by the default value isnull
. This attribute determines whether the record's cancellation token source isnull
, and if it is,null
it is of course impossible to be canceled.Register
Method: Register a callback function that will be executed when the token is canceled. Multiple callback functions can be registered, and they will be executed in the order in which they are registered. A boolean parameter can also be used to specify whether to execute the callback function synchronously.
example
// 创建一个取消令牌源
var cts = new CancellationTokenSource();
_ = DoSomethingAsync(cts.Token);
await Task.Delay(Random.Shared.Next(120, 300));
cts.Cancel();
async Task DoSomethingAsync(CancellationToken token=default)
{
token.Register(() => Console.WriteLine("被取消了"));
while (!token.IsCancellationRequested)
{
Console.WriteLine("没有被取消");
await Task.Delay(20, token);
}
}
The cancellation token can not only be used in asynchronous methods, but its cancellation callback and whether it is canceled
are enough to realize the cancellation judgment in many scenarios.
asynchronous flow
Asynchronous streams are characterized by data generation and processing that may not be synchronized and need to wait or proceed in parallel.
Asynchronous iterators are a way to implement asynchronous streams. Can be used both internally await
and yield
an async iterator takes a return IAsyncEnumerable
or IAsyncEnumerator
interface,
IAsyncEnumerable
the get iterator adds an optional parameter of the cancellation token.
IAsyncEnumerator
The GetNextValue became an asynchronous method MoveNextAsync
.
async IAsyncEnumerable<int> GetHtmlAsync()
{
// 创建一个HttpClient对象
var client = new HttpClient();
// 定义一个字符串数组,存储要访问的网址
var urls = new[] {
"www.4399.com", "www.bilibili.com", "www.baidu.com" };
// 遍历字符串数组,对每个网址发起异步请求,并返回网页源码
foreach (var url in urls)
{
// 使用await关键字来等待异步请求完成,并获取响应内容
var response = await client.GetStringAsync("http://" + url);
// 使用yield return关键字来按需返回数据
yield return response.Length;
}
}
Iterating over an asynchronous stream requires the use of await foreach
.
The same effect cannot be seen when used in the Main method . So you need to construct a method and run it at the same time.
var showTask = ShowHtmlAsync();
while (!showTask.IsCompleted)
{
Console.WriteLine("主线程等待中");
await Task.Delay(1000);
}
Console.WriteLine("主线程结束");
async Task ShowHtmlAsync()
{
Console.WriteLine("开始异步方法");
await foreach (var item in GetHtmlAsync())
{
Console.WriteLine(item);
}
Console.WriteLine("异步方法结束");
}
Whenever an asynchronous method is awaited, it will get executed foreach
.
During the waiting period, it keeps outputting that the main thread is waiting .
event driven
The c# library does not provide corresponding Linq methods for asynchronous streams, and the asynchronous stream interface was originally the content of the extension package.
This interface is added to the c# library to unify the asynchronous streams used in different extension packages to make them compatible.
Linq for asynchronous streams is often provided by extension packages, such as .Net Rx
Reactive Programming for libraries.
in the game,
- when xx gets hurt
- When teammates within 400 meters are given abnormal status
- Before the start of the turn, at the start of the turn, during the turn, at the end of the turn, after the end of the turn
- When you deal damage, before you deal damage, when you select a target, when you cast a skill.
- When the button is clicked, when the user starts sending requests.
All this kind of responding when an opportunity occurs is called an event-driven model.
At the beginning, through event registration, observer mode subscription, and asynchronous stream monitoring, decide what to do,
and then wait for the event to happen, and do not execute it if it does not happen. When it happens, execute according to the plan.