自从 C#5.0 增加异步编程之后, 异步编程越来越简单, async 和 await 用的地方越来越多, 越来越好用, 只要用异步的地方都是一连串的异步, 如果想要异步编程的时候, 需要从底层开始编写, 这样后边使用的时候就是异步, 那么底层是如何实现?? 我们如何编写高效率的异步方法??
# 了解基于任务的异步模式(TAP)
基于任务的异步编程模型 (TAP) 提供了异步代码的抽象化, 你只需像往常一样将代码编写为一连串语句即可, 在开始调用的地方运行. 例如: var task = method()1; await task2; 在1的时候开始运行可能还没有运行完, 在2程序挂起等待运行完, 中间怎么运行的你不需要知道, 编译器会做若干操作的. 当开启多个任务的时候, 像要他们都执行完, 在执行其他的时候, 可以 await Task.WhenAll(task1,task2 .....);
# 了解 async/await
await 运算符应用于异步方法, 在方法的执行中插入挂起点, 直到所等待任务完成. 使用 async 和 await 定义异步方法不一定会创建新线程, 当编译器看到 await 关键字时, 线程会挂起等待运行结束.
await 仅可用于由 async 关键字修改的异步方法中, 使用 async 修饰符定义的方法通常包含一个或多个 await 表达式, 使用 await 运算符的任务通常是实现 [基于任务的异步模式(TAP)] 的方法调用返回, 返回值包括 Task,Task<TResult>,ValueTask 和 ValueTask<TResult> 对象的方法.
# 调用 Task.Wait() 或者 Task.Result 立刻产生死锁的充分条件
1. 调用 Wait() 或 Result 的代码位于 UI 线程.
2. Task 的实际执行在其他线程, 且需要返回 UI 线程.
死锁的原因: UWP,WPF,Windows Forms 程序的 UI 线程都是单线程的. 为了避免产生死锁, 你应该一条道走到黑, Async All the Way. 或者. ConfigureAwait(false)
# ValueTask 与 Task 的区别
7.0 为 async 新增的 ValueTask 的作用(如果没有在 Nuget 上下载 System.Threading.Tasks.Extensions,ValueTask 就在这个库中),ValueTask 用于值类型的异步; Task 为引用类型的, 每次需要分配空间.
例如:
- public async Task<int> CalculateSum(int a, int b) {
- if (a == 0 && b == 0)
- {
- return 0;
- }
- return await Task.Run(() => a + b);
- }
当 a,b=0 的时候不会运行到 task 里, 这个时候返回 task 就造成了资源的浪费, 修改为以下会效率更高
- public async ValueTask<int> CalculateSum2(int a, int b)
- {
- if (a == 0 && b == 0)
- {
- return 0;
- }
- return await Task.Run(() => a + b);
- }
但是也不是说到处用 ValueTask 会好, 当是引用类型的时候, 用 ValueTask, 你需要关注更多的数据, 这个时候用 Task 会更好.
- # await/async 原理分析
- [AsyncStateMachine(typeof(Class1.<CalculateSum2>d__1))]
- public ValueTask<int> CalculateSum2(int a, int b)
- {
- Class1.<CalculateSum2>d__1 <CalculateSum2>d__;
- <CalculateSum2>d__.a = a;
- <CalculateSum2>d__.b = b;
- <CalculateSum2>d__.<>t__builder = AsyncValueTaskMethodBuilder<int>.Create();
- <CalculateSum2>d__.<>1__state = -1;
- AsyncValueTaskMethodBuilder<int> <>t__builder = <CalculateSum2>d__.<>t__builder;
- <>t__builder.Start<Class1.<CalculateSum2>d__1>(ref <CalculateSum2>d__);
- return <CalculateSum2>d__.<>t__builder.Task;
- }
对 CalculateSum2 代码解析, 发现没有 await/async, 原来又是编译器提供的语法糖.
- [__DynamicallyInvokable, DebuggerStepThrough, SecuritySafeCritical]
- public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine
- {
- if (stateMachine == null)
- {
- throw new ArgumentNullException("stateMachine");
- }
- ExecutionContextSwitcher executionContextSwitcher = default(ExecutionContextSwitcher);
- RuntimeHelpers.PrepareConstrainedRegions();
- try
- {
- ExecutionContext.EstablishCopyOnWriteScope(ref executionContextSwitcher);
- stateMachine.MoveNext();
- }
- finally
- {
- executionContextSwitcher.Undo();
- }
- }
对 Start 方法进行分析, 可以看出 MoveNext, 程序的运行其实还是一步一步进行的, 那么 await/async 会不会创建一个线程, 这倒是不一定, 这个由线程池决定, 那么异步了不创建一个线程, 怎么异步的, 这里的异步可能是运行在已经有的线程上.
来源: https://www.cnblogs.com/zhao123/p/11078382.html