由浅到深 一文看懂C#中的Async、Await关键字

2023-01-16 0 449

转自:Jeffcky http://cnblogs.com/CreateMyself/p/5983208.html

序言

以后写过相关触发器的该文,对这点始终较为弱,觉得却是不太认知,只好会花点天数去好好学习这几块,他们循序渐进,该文若有描述不科学合理含意,还请Behren。

热门话题

呢将方式用asyncURL标记是触发器方式了呢?

呢没awaitURL的存有async就没存有的象征意义了呢?

用触发器方式的前提是甚么呢,为何会有那个前提管制?

根本无法初始化.NET Framework内建的用await标记的Task,若想自订同时实现呢?

在lambda函数中与否能用async和awaitURL来同时实现触发器呢(即触发器lambda函数)?

前述放出这两个热门话题,知道责任编辑主要就讲诉的热门话题和须要深入细致介绍的科学知识。

asyncURL

比如触发器方式是此种的:

public static async Task<int> asyncMethod() { return await Task.Run(() => Calculate()); } static int Calculate() { return 1 + 1; }

那就算如下表所示此种写呢?

public static async Task<int> asyncMethod() { var task = Task.Run(() => Calculate()); return task.Result; }

那上述此种读法呢也是触发器方式呢?标准答案是【NO】,难道并非触发器方式为何要用asyncURL来展开标记呢?并非很难被他们所误会呢?

好了疑问这么多他们一一来解惑。

当方式用async标记时,编译器主要就做了甚么呢?

1、告诉编译器那个方式里面可能会用到awaitURL来标记该方式是触发器的,如此之后,编译器将会在状态机中编译此方式。接着该方式执行到awaitURL时会处于挂起的状态直到该触发器动作完成后才恢复继续执行方式后面的动作。

2、告诉编译器解析出方式的结果到返回类型中,比如说Task或者Task<TResult>,也是说将返回值存储到Task中,如果返回值为void那么此时应该会将可能出现的异常存储到上下该文。

当方式用async标记时,呢所有初始化者都将是触发器的呢?

当将方式用async标记时且返回值为void或者Task或者Task<TReuslt>,此时该方式会在当前线程中始终同步执行。

用async标记方式并不会影响方式运行完成与否是同步或者触发器,相反,它能够将方式划分成多块,有可能有些在触发器中运行,以至于这些方式是触发器完成的,而划分触发器和同步方式的边界是使用awaitURL。也是说如果在方式中未用到awaitURL时则该方式是一整块没所谓的划分,会在同步中运行,在同步中完成。

当方式用async标记时,与否会引起方式的初始化会被添加到线程池队列中或者是创建一个新的线程呢?

显然并非此种,当用async标记方式时只是显示告诉编译器在该方式中awaitURL可能会被用到,当执行到awaitURL开始处于挂起的状态知道触发器动作执行完成才恢复(触发器操作是在状态机中【相关状态机请看这里:Async和Await触发器编程的原理】完成,完成后此时才会创建一个线程),这也是为何在方式中方式用async标记如果没用到awaitURLIDE会发出警告的原因。

到了这里他们能得出结论:无论方式是同步却是触发器都能用asyncURL来展开标记,因为用async标记只是显示表明在该方式内可能会用到awaitURL使其变为触发器方式,而且将该触发器方式展开了明确的划分,只有用了awaitURL时才是触发器操作,其余一并为同步操作。

参数为何不能使用ref和outURL

返回类型必须为void或者Task或者Task<TResult>和URL的标记和其他就不再描述,其中有一条是不能使用ref和outURL,你是背书似的铭记了这一条,却是略加思索了呢?你想过没为何不可呢?

他们知道用ref和outURL不过是为了在方式里面改变其值,也是是当同步完成时他们期望被ref或者outURL修饰的值会被设置,但是它们可能在触发器完成时或者之后才会被设置达不到他们预期,所以在触发器方式中不能用ref和outURL。

lambda函数与否能触发器呢?

返回类型Task参数能为lambda函数或者匿名方法对象,那直接对lambda函数触发器与否可行?下面他们来看看

public static async Task<T2> CallFuncAsync<T1, T2>(T1 t, Func<T1, T2> func) { return func.Invoke(t); } public static async Task<string> GetStringAsync(int value) { return await Task.Run(() => “xpy0928”); } public static async Task MainAsync() { string value = await CallFuncAsync<int, string>(30, async (s) => await GetStringAsync(s)); }

编译后生成如下表所示错误:

由浅到深 一文看懂C#中的Async、Await关键字

由上知触发器lambda函数是不行的,猜测是触发器lambda表达式不能转换为函数树,同时他们看看前述代码,CallFunAsync此时并未是触发器方式,前述他们已经描述过,此时是同步运行,难道前述错误,并且代码也有不可取含意他们接下来一一展开修改。

string value = await CallFuncAsync<int, string>(30, async (s) => await GetStringAsync(s));

修改为:

string value = await CallFuncAsync<int, string>(30, s => GetStringAsync(s).Result);

解决了编译错误,但是未解决CallFuncAsync为触发器运行,他们将其修改为触发器运行。难道await是针对于Task而操作,他们将CallFuncAsync中的返回参数设置为Task即可。

public static async Task<T2> CallFuncAsync<T1, T2>

则最终初始化时他们直接初始化即可。

string value = await CallFuncAsync(30, GetStringAsync);

此时CallFuncAsync才算是触发器运行。

补充

对于触发器函数有一点其实表述不太正确,其实我始终却是有点怀疑触发器lambda函数真的不行吗,此刻我居然发现此种是能的:

var task = Task.Run(async () => { await Task.Delay(TimeSpan.FromMilliseconds(5000)); });

如上不正是触发器函数的影子吗,只好我将前述代码展开了改写,如下表所示:

public static async Task<Action> CallFuncAsync(Action action) { return action; } public static async Task<Action> GetStringAsync() { return () => Console.WriteLine(“xpy0928”); } public static async Task MainAsync() { await CallFuncAsync(async () => await GetStringAsync()); }

此时编译通过,说明表述触发器函数并非一定不能,只是对于无参数的lambda函数才能,而对于有参数的lambda函数如fun则不能执行触发器lambda函数。

至此能基本下结论:

触发器lambda函数只对于无参数的lambda函数 才能(当然这也就没了甚么象征意义),而对于有参数的lambda函数则产生编译错误则不能执行触发器(猜测是无法转换成对应的函数树)。(不知与否严谨或者不妥,若有错误含意,还望对此认知的更透彻的园友给出批评性意见)。

为了验证这一点,他们来看看无参数的func委托例子,如下表所示:

static async Task<string> GetTaskAsync() { await Task.Delay(TimeSpan.FromMilliseconds(5000)); return “xpy0928”; } var task = Task.Run(async () => await GetTaskAsync());

此时无参数的func委托则编译通过,应该是验证了前述观点(却是有点怀疑我所下的结论)。

awaitURL

awaitURL是此种用的

await Task.Run(() => “xpy0928”);

此时背后究竟发生了甚么呢?他们前述也说过触发器动作时在状态机中完成,当执行到这里时,编译器会自动生成代码来检测该动作与否已经完成,如果已经完成则继续同步执行awaitURL后面的代码,通过判断其状态机状态若未完成则会挂起一个继续的委托为awaitURL的对象直到完成为止,初始化那个继续动作的委托重新进入未完成的此种一个方式。

比如说: await someObject; 编译器则会生成如下表所示代码:

private class FooAsyncStateMachine : IAsyncStateMachine { // Member fields for preserving “locals” and other necessary state int $state; TaskAwaiter $awaiter; … public void MoveNext() { // Jump table to get back to the right statement upon resumption switch (this.$state) { … case 2: goto Label2; … } … // Expansion of “await someObject;” this.$awaiter = someObject.GetAwaiter(); if (!this.$awaiter.IsCompleted) { this.$state = 2; this.$awaiter.OnCompleted(MoveNext); return; Label2: } this.$awaiter.GetResult(); … } }

此时讲到这里就要涉及到await背后具体的同时实现,在Task或者Task<TResult>类里面有此种一个返回类型为 TaskAwaiter 的 GetAwaiter 属性,而TaskAwaiter中有如下表所示属性:

public struct TaskAwaiter : ICriticalNotifyCompletion, INotifyCompletion { public bool IsCompleted { get; } public void GetResult(); public void OnCompleted(Action continuation); public void UnsafeOnCompleted(Action continuation); }

通过IsComplete来判断与否已经完成。那个有甚么作用呢?通过看到背后具体实现,他们能自己简单同时实现触发器扩展方式,当他们在Task中查看其方式会有此种的提示:

由浅到深 一文看懂C#中的Async、Await关键字

下面他们就来同时实现此种的效果,给TimeSpan添加触发器方式:

public static class Extend { public static TaskAwaiter GetAwaiter(this TimeSpan timeSpan) { return Task.Delay(timeSpan).GetAwaiter(); } }

此时触发器方式则是此种的:

由浅到深 一文看懂C#中的Async、Await关键字

总结

本节他们详细讲诉了async和awaitURL的使用和一些基本原理和解释其原因,希望通过对责任编辑的学习,对大家能够更好的去认知触发器。

其他推荐:

玩转Github:ASP.NET Core入门学习资源汇总

学习C#有没甚么较为系统的资源?

.net core高频面试题有哪些?

玩转Github:强烈推荐这份.NET程序员面试手册,4万字干货!

有哪些不错的windows form开源项目推荐?

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务