c# notes - asynchronous

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 returncan 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.asyncawait
Task
Task

asyncIs a method modifier that Taskcan be used in any returning method or anonymous delegate.
The modified method will automatically split into multiples according to the content Task, and then Taskgive you the last one as the return value.

awaitSimilar to yield return, is the basis for segmentation. The asynchronous method will awaitsplit 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 asyncthat 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 voidor Task, this is a method with no return value.
The writing inside the method voidis the same as the return method, and you cannot returnfollow it with a value.

If your return type is Task<int>, then the inside of the method is written like the returned intmethod,
you must returnfollow it with a intto end the method.

Unlike iterators, it does not awaitdecide whether to automatically synthesize based on whether you have written it.
As long as you use asyncthe 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, outotherwise 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, awaitthe syntax can be used as long as certain conditions are met.
These conditions also hint at the principle of asynchow 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; }: boolA 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 foreachsomeone 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 returnending the current method or awaitsuspending 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 awaitthe 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.YieldIt is the fastest asynchronous task, its role is really just to give up the execution right,
it can be said that once awaitit 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, awaitbut waiting for this task to complete.
So if you want to create and wait separately, you need to save this Taskand only when needed await.

async method scope

The entire method of an asynchronous method is the same scope.
If variables are declared inside using, awaitthey will not be released until they are finished.
Some file read operations and network access operations are returned Task.
But if you use usingdeclarations for them, you must complete the wait before returning the value.
If you directly use what you get from them Taskas 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 awaitparsing 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, awaitparsed) .

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-catchsurrounded 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 asyncmodified.
Or, override the return Taskmethod of the base class, but do not need to use it await.

asyncIt 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.CompletedTaskwill give you a completed task
  • Task.FromResultmethod or create a completed task with the completion value you give
  • Task.FromCanceledmethod creates a canceled task from the specified cancellation token.
  • Task.FromExceptionmethod creates a task that throws an exception from the specified exception.

This also corresponds to one Taskof the four end situations. CompletedTaskand FromResultindicate the successful completion of the task,
respectively, no return value and return value Task.

FromCanceledand FromExceptionrepresent 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 Tasksaved.
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.DelayThe 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 awaitsuspended.

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.Runto 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

TaskThe method of the instance ContinueWithcreates 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);
});

fileTaskIt's httpTaska 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, Taskyou 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.WhenAllMultiple 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.WhenAnyMultiple 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 Taskis 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 Waitmethod 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 Resultto 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 Waitor Resultwaiting.
So if there may be exceptions, you need to put Waitor Resultin try.

var taskResult=Task.FromResult(12);
taskResult.Wait(); 
int Result=taskResult.Result;

For methods of waiting for multiple tasks together, there are also Task.WaitAlland Task.WaitAny.
They are used in the same way as Task.WhenAll, Task.WhenAnyexcept that instead of returning Task, they wait.

task completion

Among the properties of a task instance, there are the following 4 boolproperties

  • IsCanceled: task was canceled
  • IsFaulted: An exception occurred in the task
  • IsCompletedSuccessfully: run successfully to the end
  • IsCompleted: The task has finished running, regardless of success, cancellation, or exception.

In addition to these final state properties, there is another Statusproperty 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.Runor TaskFactory.StartNewmethod 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.
调用Start方法
安排到任务调度器中
开始执行委托
执行完成
被取消
抛出异常
有附加的子任务
Created
WaitingToRun
WaitingForActivation
Running
RanToCompletion
Canceled
Faulted
WaitingForChildrenToComplete

Cancel

The cancellation operation is related to two classes, the cancellation token source CancellationTokenSourceand 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 Asyncends with and has an optional parameter that accepts a cancellation token.
Cancellation tokens are value types, so using them directly defaultyields 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:

  • TokenProperty: Returns a cancellation token associated with this source, which can be passed to asynchronous methods that need to support cancellation.
  • CancelMethod: 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 throw OperationCanceledExceptionexceptions.
  • CancelAfterMethod: After a specified time interval, Cancelthe method is called automatically. This can be used to implement timeout functionality.
  • CreateLinkedTokenSourceStatic 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:

  • IsCancellationRequestedProperty: Returns a boolean indicating whether cancellation of the operation has been requested. If true, the operation has been requested to be cancelled, otherwise, false.
  • CanBeCanceledAttributes: 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 is null. This attribute determines whether the record's cancellation token source is null, and if it is, nullit is of course impossible to be canceled.
  • RegisterMethod: 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 awaitand yield
an async iterator takes a return IAsyncEnumerableor IAsyncEnumeratorinterface,
IAsyncEnumerablethe get iterator adds an optional parameter of the cancellation token.
IAsyncEnumeratorThe 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 RxReactive 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.

Guess you like

Origin blog.csdn.net/zms9110750/article/details/130747591