【C# TAP 异步编程】二 、await运算符已经可等待类型Awaitable
await的作用:
1、await是一个标记,告诉编译器生成一个等待器来等待可等待类型实例的运行结果。
2、一个await对应一个等待器 ,任务的等待器类型是TaskAwaiter/TaskAwaiter<TResult>。
3、Await 就像一个一元运算符:它接受一个参数,一个可等待的"awaitable"类型的实例。它暂停对其所属的 async 方法的求值,直到其 【操作数】 表示的异步操作完成。 异步操作完成后,await 运算符将返回操作的结果(如果有)。
await task等效于task.GetAwaiter().GetResult()。task.GetAwaiter() 返回TaskAwaiter/TaskAwaiter<TResult>
4、可等待类型awaitable:Task、Task<TResult>、ValueTask、Task.Yield()、ConfiguredTaskAwaitable ConfigureAwait()、包含GetAwaiter()的鸭子类型
等待者模式
Await/Async 异步使模式使用的是等待者模式。等待者模式要求等待者公开IsCompleted属性,GetResult方法和OnCompleted方法(可选地带有UnsafeOnCompleted方法,unsafe表示不使用可执行上下 回导致漏洞)。
await用法
await 类型实例TTT
该【类型实例TTT】的类型必须包含 GetAwaiter()方法,并且GetAwaiter()方法的返回值类型继承INotifyCompletion接口,实现属性public bool IsCompleted { get; },方法public void GetResult() { }。使用方法和枚举器IEnumerable、 Enumerator一样。
例如:Task类包含GetAwaiter()方法,GetAwaiter()方法的返回值TaskAwaiter类 继承INotifyCompletion接口,TaskAwaiter类实现属性IsCompleted和方法GetResult()。
//Task类型
public TaskAwaiter GetAwaiter()
{
return new TaskAwaiter(this);
}
///TaskAwaiter类 实现了属性IsCompleted、方法GetResult()以及INotifyCompletion接口
明白原理后,自定义一个类
using System.Runtime.CompilerServices; Task t = AsynchronousProcessing();
t.Wait();
Console.ReadLine();
static async Task AsynchronousProcessing()
{
var sync = new CustomAwaitable(true);
string result = await sync;
// Completed synchronously
Console.WriteLine(result); var async = new CustomAwaitable(false);
result = await async;
// Task is running on a thread id 3. Is thread pool thread: True
Console.WriteLine(result);
} /// <summary>
/// 类型t
/// </summary>
class CustomAwaitable
{
private readonly bool _completeSynchronously; public CustomAwaitable(bool completeSynchronously)
{
_completeSynchronously = completeSynchronously;
} /// <summary>
/// t有一个名为GetAwaiter的可访问的实例或扩展方法
/// </summary>
/// <returns>类型A</returns>
public CustomAwaiter GetAwaiter()
{
return new CustomAwaiter(_completeSynchronously);
}
} /// <summary>
/// 类型A 实现了 System.Runtime.CompilerServices.INotifyCompletion 接口
/// </summary>
class CustomAwaiter : INotifyCompletion
{
private string _result = "Completed synchronously";
private readonly bool _completeSynchronously; /// <summary>
/// A有一个可访问的、可读的类型为bool的实例属性IsCompleted
/// 如果IsCompleted属性返回true,则只需同步调用GetResult方法
/// </summary>
public bool IsCompleted => _completeSynchronously; public CustomAwaiter(bool completeSynchronously)
{
_completeSynchronously = completeSynchronously;
} /// <summary>
/// A有一个名为GetResult的可访问的实例方法,该方法没有任何参数和类型参数。
/// </summary>
/// <returns></returns>
public string GetResult()
{
return _result;
} public void OnCompleted(Action continuation)
{
ThreadPool.QueueUserWorkItem(state =>
{
Thread.Sleep(TimeSpan.FromSeconds(1));
_result = GetInfo();
continuation?.Invoke();
});
} private string GetInfo()
{
return $"Task is running on a thread id {Thread.CurrentThread.ManagedThreadId}. " +
$"Is thread pool thread: {Thread.CurrentThread.IsThreadPoolThread}";
} }
await 的异步方法的刨析
通过一段代码来了解await,把这编译后,用ILspy 反编译。
namespace MyTask;
class Program
{
public static void Main(string[] args)
{
Task<string> baconTask = FryBaconAsync(3);
baconTask.ContinueWith(t =>Console.WriteLine(t.Result));
Console.Read();
}
static async Task<string> FryBaconAsync(int slices)
{
HttpClient httpClient=new HttpClient(); string content = await httpClient.GetStringAsync("https://www.cnblogs.com/cdaniu/p/15681416.html");
return content; //整数3和Task<int>不存在隐形转化啊,怎么就可以return 3; 如果你也存在这个疑问 请继续往下阅读,接下去详细分析。
}
}
用ILspy 反编译后的完整代码:

// MyTask.Program
using System;
using System.Diagnostics;
using System.Net.Http;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using MyTask; [System.Runtime.CompilerServices.NullableContext(1)]
[System.Runtime.CompilerServices.Nullable(0)]
internal class Program
{
[Serializable]
[CompilerGenerated]
private sealed class <>c
{
[System.Runtime.CompilerServices.Nullable(0)]
public static readonly <>c <>9 = new <>c(); [System.Runtime.CompilerServices.Nullable(new byte[] { 0, 1, 1 })]
public static Action<Task<string>> <>9__0_0; internal void <Main>b__0_0(Task<string> t)
{
Console.WriteLine(t.Result);
}
} [CompilerGenerated]
private sealed class <FryBaconAsync>d__1 : IAsyncStateMachine
{
public int <>1__state; [System.Runtime.CompilerServices.Nullable(0)]
public AsyncTaskMethodBuilder<string> <>t__builder; public int slices; [System.Runtime.CompilerServices.Nullable(0)]
private HttpClient <httpClient>5__1; [System.Runtime.CompilerServices.Nullable(0)]
private string <content>5__2; [System.Runtime.CompilerServices.Nullable(0)]
private string <>s__3; [System.Runtime.CompilerServices.Nullable(new byte[] { 0, 1 })]
private TaskAwaiter<string> <>u__1; private void MoveNext()
{
int num = <>1__state;
string result;
try
{
TaskAwaiter<string> awaiter;
if (num != 0)
{
<httpClient>5__1 = new HttpClient();
awaiter = <httpClient>5__1.GetStringAsync("https://www.cnblogs.com/cdaniu/p/15681416.html").GetAwaiter();
if (!awaiter.IsCompleted)
{
num = (<>1__state = 0);
<>u__1 = awaiter;
<FryBaconAsync>d__1 stateMachine = this;
<>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine);
return;
}
}
else
{
awaiter = <>u__1;
<>u__1 = default(TaskAwaiter<string>);
num = (<>1__state = -1);
}
<>s__3 = awaiter.GetResult();
<content>5__2 = <>s__3;
<>s__3 = null;
result = <content>5__2;
}
catch (Exception exception)
{
<>1__state = -2;
<httpClient>5__1 = null;
<content>5__2 = null;
<>t__builder.SetException(exception);
return;
}
<>1__state = -2;
<httpClient>5__1 = null;
<content>5__2 = null;
<>t__builder.SetResult(result);
} void IAsyncStateMachine.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
this.MoveNext();
} [DebuggerHidden]
private void SetStateMachine(IAsyncStateMachine stateMachine)
{
} void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine)
{
//ILSpy generated this explicit interface implementation from .override directive in SetStateMachine
this.SetStateMachine(stateMachine);
}
} public static void Main(string[] args)
{
Task<string> baconTask = FryBaconAsync(3);
baconTask.ContinueWith(<>c.<>9__0_0 ?? (<>c.<>9__0_0 = new Action<Task<string>>(<>c.<>9.<Main>b__0_0)));
Console.Read();
} [AsyncStateMachine(typeof(<FryBaconAsync>d__1))]
[DebuggerStepThrough]
private static Task<string> FryBaconAsync(int slices)
{
<FryBaconAsync>d__1 stateMachine = new <FryBaconAsync>d__1();
stateMachine.<>t__builder = AsyncTaskMethodBuilder<string>.Create();
stateMachine.slices = slices;
stateMachine.<>1__state = -1;
stateMachine.<>t__builder.Start(ref stateMachine);
return stateMachine.<>t__builder.Task;
}
}
编译器根据await生成一个等待器awaiter,用这个awaiter 等待异步方法运行的结果。然后程序通过return;把控制权还给了调用函数,
可等待的数据类型-Awaitable Type
编译器采用鸭子类型来判断可等待类型。即一个Type包含 public WorkItemAwaiter GetAwaiter()方法就是可等待类型。返回值WorkItemAwaiter的类型必须继承INotifyComplete接口和有 public void GetResult() 方法、 public bool IsCompleted{get;}属性。和枚举器(Enumerable、Enumerator)的原理一样
自定义一个可等待的数据类型 WorkItem
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
Debug.WriteLine("sfsdfs");
Test(); async WorkItem Test()
{ await new WorkItem(); }
public class WorkItem
{
public WorkItemAwaiter GetAwaiter()
{
return new WorkItemAwaiter(this);
}
internal volatile int m_StateFlag; public bool IsCompleted
{
get
{
int stateFlag = m_StateFlag;
return IsCompletedMethod(stateFlag);
}
} private bool IsCompletedMethod(int stateFlag)
{
//判断完成
return (stateFlag & (int)TestStateFlag.Completed) != 0;
} // m_StateFlag的常量
internal enum TestStateFlag
{
Start = 0x0001, // bin: 0000 0000 0000 0000 0000 0000 0000 0001
Running = 0x0002, // bin: 0000 0000 0000 0000 0000 0000 0000 0010
Waiting = 0x0004, // bin: 0000 0000 0000 0000 0000 0000 0000 0100
Completed = 0x0008, // bin: 0000 0000 0000 0000 0000 0000 0000 1000 WaitCompleteNotifyed = 0x0010, // bin: 0000 0000 0000 0000 0000 0000 0001 0000 }
//
internal bool IsWaitNotificationEnabledOrNotCompleted =>
(m_StateFlag & ((int)TestStateFlag.WaitCompleteNotifyed | (int)TestStateFlag.Completed)) != (int)TestStateFlag.Completed;
}
/// <summary>
///
/// </summary>
public class WorkItemAwaiter : ICriticalNotifyCompletion
{
internal readonly WorkItem m_WorkItem; public WorkItemAwaiter(WorkItem workItem)
{
this.m_WorkItem = workItem;
}
public bool IsCompleted=> m_WorkItem.IsCompleted; public void OnCompleted(Action continuation)
{
throw new NotImplementedException();
} public void UnsafeOnCompleted(Action continuation)
{
throw new NotImplementedException();
} public void GetResult()
{
ValidateEnd(m_WorkItem);
}
/// <summary>
///快速检查一个await操作是否结束,以确定在完成await操作之前是否需要做更多的操作。
/// </summary>
/// <param name="task">The awaited task.</param>
internal static void ValidateEnd(WorkItem workItem)
{
// 快速检测
if (workItem.IsWaitNotificationEnabledOrNotCompleted)
{ } }
internal void SpinUntilCompleted()
{
// Spin wait until the completion is finalized by another thread.
SpinWait sw = default;
while (!IsCompleted)
{
sw.SpinOnce();
}
} }
【C# TAP 异步编程】二 、await运算符已经可等待类型Awaitable的更多相关文章
- 温故知新,CSharp遇见异步编程(Async/Await),聊聊异步编程最佳做法
什么是异步编程(Async/Await) Async/Await本质上是通过编译器实现的语法糖,它让我们能够轻松的写出简洁.易懂.易维护的异步代码. Async/Await是C# 5引入的关键字,用以 ...
- 异步编程Async/await关键字
异步编程Async \await 关键字在各编程语言中的发展(出现)纪实. 时间 语言版本 2012.08.15 C#5.0(VS2012) 2015.09.13 Python 3.5 2016.03 ...
- 抓住异步编程async/await语法糖的牛鼻子: SynchronizationContext
长话短说,本文带大家抓住异步编程async/await语法糖的牛鼻子: SynchronizationContext 引言 C#异步编程语法糖async/await,使开发者很容易就能编写异步代码. ...
- 【C# TAP 异步编程】三、async\await的运作机理详解
[原创] 本文只是个人笔记,很多错误,欢迎指出. 环境:vs2022 .net6.0 C#10 参考:https://blog.csdn.net/brook_shi/article/details/ ...
- 异步编程,await async入门
网上很多异步编程的文章,提供一篇入门: 异步编程模型 .net支持3种异步编程模式: msdn:https://docs.microsoft.com/zh-cn/dotnet/standard/asy ...
- 【憩园】C#并发编程之异步编程(二)
写在前面 前面一篇文章介绍了异步编程的基本内容,同时也简要说明了async和await的一些用法.本篇文章将对async和await这两个关键字进行深入探讨,研究其中的运行机制,实现编码效率与运行效率 ...
- .net 温故知新:【5】异步编程 async await
1.异步编程 异步编程是一项关键技术,可以直接处理多个核心上的阻塞 I/O 和并发操作. 通过 C#.Visual Basic 和 F# 中易于使用的语言级异步编程模型,.NET 可为应用和服务提供使 ...
- C#~异步编程再续~await与async引起的w3wp.exe崩溃
返回目录 最近怪事又开始发生了,IIS的应用程序池无做挂掉,都指向同一个矛头,async,threadPool,Task,还有一个System.NullReferenceException,所以这些都 ...
- C#异步编程二
上一异步编程的博客还是在9月份的,最近事情也比较多,烦恼事情一个接着一个,一个人的周末除了无聊就剩无聊了,也只有写写博客来打发下这无聊的时光.原本想着尽快把异步编程这块总结一下,之前把委托异步算是总结 ...
随机推荐
- 解决Post请求中文乱码问题
解决Post请求中文乱码问题 req.setChracterEncoding()要在获取请求参数前调用才有效,不然还是乱码
- IDEA2020.1破解
IDEA2020.1破解 安装 下载idea idea官方下载地址:https://www.jetbrains.com/webstorm/download/other.html 下载破解插件 链接:h ...
- 【程序15】成绩>=90分用A表示,60-89分用B表示, 60分以下用C表示。
利用条件运算符的嵌套来完成此题 score = int(input('input score:')) if score >= 90: grade = 'A' elif score >= 6 ...
- 使用Termux并与ubuntu建立ssh连接
什么是Termux? Termux是一个Android终端仿真器和Linux环境应用程序,直接工作,无需根目录或设置.一个最小的基本系统被自动安装-额外的软件包可以使用APT软件包管理器来使用.不需要 ...
- 面向次世代的Windows App SDK 近况
Project Reunion作为面向次世代Windows App Development的统一工具集.在2021年11月,第三个稳定版正式以1.0的版本号发布的同时,改名部果断出手,以全新的名称Wi ...
- Java多线程专题6: Queue和List
合集目录 Java多线程专题6: Queue和List CopyOnWriteArrayList 如何通过写时拷贝实现并发安全的 List? CopyOnWrite(COW), 是计算机程序设计领域中 ...
- js文件中三斜杠注释///reference path的用途
编辑某个js文件时,要想这个js文件出现其他js成员的ide提示,可以在js文件开头使用3个斜杠注释和reference指令的path指向此js文件路径,这样在编写这个js文件时,ide就会自动出现p ...
- Zookeeper应用场景汇总(超详细)
Zookeeper典型应用场景汇总 数据发布与订阅(配置中心) 发布与订阅模型,即所谓的配置中心,顾名思义就是发布者将数据发布到ZK节点上,供订阅者动态获取数据,实现配置信息的集中式管理和动态更新.例 ...
- nginx103
user nobody;worker_processes 1;error_log /home/logs/error.log info;#pid logs/nginx.pid;ev ...
- 自定义CALayer
1.如何自定义Layer. 自定义CALayer的方式创建UIView的方式非常相似. CALayer *layer = [CALayer layer]; layer.fr ...