异步函数简介

一般指 async 修饰符声明得、可包含await表达式得方法或匿名函数。

声明方式

异步方法的声明语法与其他方法完全一样, 只是需要包含 async 关键字。async可以出现在返回值之前的任何位置, 如下示例:

  1. async public static void GetInfoAsync()
  2. {
  3. //...
  4. }
  5. public async static void GetInfoAsync()
  6. {
  7. //...
  8. }
  9. public static async void GetInfoAsync()
  10. {
  11. //...
  12. }

异步方法的返回类型
异步函数的返回类型只能为: void、Task、Task
Task: 代表一个返回值T类型的操作。
Task: 代表一个无返回值的操作。
void: 为了和传统的事件处理程序兼容而设计。

await(等待)

await等待的是什么? 可以是一个异步操作(Task)、亦或者是具备返回值的异步操作(Task)的值, 如下:

  1. public async static void GetInfoAsync()
  2. {
  3. await GetData(); // 等待异步操作, 无返回值
  4. await GetData<int>(1); //等待异步操作, 返回值 int
  5. }
  6. static Task GetData()
  7. {
  8. //...
  9. return null;
  10. }
  11. static Task<T> GetData<T>(int a)
  12. {
  13. //...
  14. return null;
  15. }

注: await 最终操作的是一个值, 当然, 也可以是无值, 如上GetData() , 否则就是一个 Task 如上: GetData()

await执行过程

异步编程:async/await深入理解 - 图1

TaskAwaiter 获取执行结果

一般而言, await等待的一个异步操作, 无论是具备返回值还是否, 那么最终都会获得该操作是否已完成、具备返回值得异步操作可以获取他得返回结果。
所以这个时候, TaskAwaiter出现了, 无论是Task、还是Task操作, 都具备GetAwaiter() 方法。
用于获取改操作得状态、返回结果, 及部分操作, 如下TaskAwaiter结构:

  1. //
  2. // 摘要:
  3. // 提供等待异步任务完成的对象。
  4. public struct TaskAwaiter : ICriticalNotifyCompletion, INotifyCompletion
  5. {
  6. //
  7. // 摘要:
  8. // 获取一个值,该值指示是否已完成的异步任务。
  9. //
  10. // 返回结果:
  11. // true 如果任务已完成;否则为 false。
  12. //
  13. // 异常:
  14. // T:System.NullReferenceException:
  15. // System.Runtime.CompilerServices.TaskAwaiter 对象未正确初始化。
  16. public bool IsCompleted { get; }
  17. //
  18. // 摘要:
  19. // 结束异步任务完成之前的等待。
  20. //
  21. // 异常:
  22. // T:System.NullReferenceException:
  23. // System.Runtime.CompilerServices.TaskAwaiter 对象未正确初始化。
  24. //
  25. // T:System.Threading.Tasks.TaskCanceledException:
  26. // 任务已取消。
  27. //
  28. // T:System.Exception:
  29. // 在完成的任务 System.Threading.Tasks.TaskStatus.Faulted 状态。
  30. [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
  31. public void GetResult();
  32. //
  33. // 摘要:
  34. // 设置时应执行的操作 System.Runtime.CompilerServices.TaskAwaiter 对象停止等待异步任务完成。
  35. //
  36. // 参数:
  37. // continuation:
  38. // 要在等待操作完成时执行的操作。
  39. //
  40. // 异常:
  41. // T:System.ArgumentNullException:
  42. // continuation 为 null。
  43. //
  44. // T:System.NullReferenceException:
  45. // System.Runtime.CompilerServices.TaskAwaiter 对象未正确初始化。
  46. [SecuritySafeCritical]
  47. public void OnCompleted(Action continuation);
  48. //
  49. // 摘要:
  50. // 计划程序与此等待异步任务的延续任务操作。
  51. //
  52. // 参数:
  53. // continuation:
  54. // 要等待操作完成时调用的操作。
  55. //
  56. // 异常:
  57. // T:System.ArgumentNullException:
  58. // continuation 为 null。
  59. //
  60. // T:System.InvalidOperationException:
  61. // 该等待程序未正确初始化。
  62. [SecurityCritical]
  63. public void UnsafeOnCompleted(Action continuation);
  64. }

接下来, 演示如何通过等待去获取异步操作的返回结果, 如下代码所示:

  1. public async static void GetInfoAsync()
  2. {
  3. Task<bool> task = Task.Run<bool>(() =>
  4. {
  5. Thread.Sleep(10000); //模拟耗时
  6. return true;
  7. });
  8. //以下两种方式
  9. bool taskResult1 = await task; //内部自己执行了GetAwaiter()
  10. bool taskResult = task.GetAwaiter().GetResult(); //自己手动执行Awaiter(), 但是阻塞UI
  11. Console.WriteLine(taskResult);
  12. }

注: 对于一个await表达式, 编译器生成的代码会先调用GetAwaiter(), 然后通过awaiter得成员来等待结果, 所以以上两种方式等效( 不考虑阻塞的情况下)
为了验证以上猜测, 通过反编译工具查看得到如下代码:异步编程:async/await深入理解 - 图2编译器最终生成两个密封类, 一个类( <>c )我们展开分析:
b__1_0() 正是模拟耗时的一个操作委托生成的方法。

  1. [CompilerGenerated]
  2. [Serializable]
  3. private sealed class <>c
  4. {
  5. public static readonly Program.<>c <>9 = new Program.<>c();
  6. public static Func<bool> <>9__1_0;
  7. internal bool <GetInfoAsync>b__1_0()
  8. {
  9. Thread.Sleep(10000);
  10. return true;
  11. }
  12. }

第二个类 d1 分析:
该类分别实现了接口 IAsyncStateMachine 的MoveNext() 与 SetStateMachine() ,另外 注意,
还特别定义了一个 <>**t
builder**, 先记住他, 下面讲会对他讲到, 为什么编译器生成的代码会默认先调用GetAwaiter()

  1. [CompilerGenerated]
  2. private sealed class <GetInfoAsync>d__1 : IAsyncStateMachine
  3. {
  4. public int <>1__state;
  5. public AsyncVoidMethodBuilder <>t__builder;
  6. private Task<bool> <task>5__1;
  7. private bool <result>5__2;
  8. private bool <>s__3;
  9. private TaskAwaiter<bool> <>u__1;
  10. void IAsyncStateMachine.MoveNext()
  11. {
  12. int num = this.<>1__state;
  13. try
  14. {
  15. TaskAwaiter<bool> awaiter;
  16. if (num != 0)
  17. {
  18. Func<bool> arg_2F_0;
  19. if ((arg_2F_0 = Program.<>c.<>9__1_0) == null)
  20. {
  21. arg_2F_0 = (Program.<>c.<>9__1_0 = new Func<bool>(Program.<>c.<>9.<GetInfoAsync>b__1_0));
  22. }
  23. this.<task>5__1 = Task.Run<bool>(arg_2F_0);
  24. awaiter = this.<task>5__1.GetAwaiter();
  25. if (!awaiter.IsCompleted)
  26. {
  27. this.<>1__state = 0;
  28. this.<>u__1 = awaiter;
  29. Program.<GetInfoAsync>d__1 <GetInfoAsync>d__ = this;
  30. this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<bool>, Program.<GetInfoAsync>d__1>(ref awaiter, ref <GetInfoAsync>d__);
  31. return;
  32. }
  33. }
  34. else
  35. {
  36. awaiter = this.<>u__1;
  37. this.<>u__1 = default(TaskAwaiter<bool>);
  38. this.<>1__state = -1;
  39. }
  40. this.<>s__3 = awaiter.GetResult();
  41. this.<result>5__2 = this.<>s__3;
  42. Console.WriteLine(this.<result>5__2);
  43. }
  44. catch (Exception exception)
  45. {
  46. this.<>1__state = -2;
  47. this.<>t__builder.SetException(exception);
  48. return;
  49. }
  50. this.<>1__state = -2;
  51. this.<>t__builder.SetResult();
  52. }
  53. [DebuggerHidden]
  54. void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine)
  55. {
  56. }
  57. }

接下来, 看GetInfoAsync()方法, 这个是自己编写的, 但是实现的细节,最终转换成了编译器执行代码:

  1. [AsyncStateMachine(typeof(Program.<GetInfoAsync>d__1)), DebuggerStepThrough]
  2. public static void GetInfoAsync()
  3. {
  4. Program.<GetInfoAsync>d__1 <GetInfoAsync>d__ = new Program.<GetInfoAsync>d__1();
  5. <GetInfoAsync>d__.<>t__builder = AsyncVoidMethodBuilder.Create();
  6. <GetInfoAsync>d__.<>1__state = -1;
  7. AsyncVoidMethodBuilder <>t__builder = <GetInfoAsync>d__.<>t__builder;
  8. <>t__builder.Start<Program.<GetInfoAsync>d__1>(ref <GetInfoAsync>d__); //注意到该代码, 调用了Start(),也许这就是默认实现的地方
  9. }

通过查看Start泛型方法的实现, 最终找到了, 该泛型的条件限制于必须实现与 IAsyncStateMachine 接口, 所以通过查看, 该类最终调用了 MoveNext(), 而MoveNext中正
调用了GetAwaiter()。关于Start的实现如下所示:

  1. [SecuritySafeCritical, DebuggerStepThrough, __DynamicallyInvokable]
  2. public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine
  3. {
  4. if (stateMachine == null)
  5. {
  6. throw new ArgumentNullException("stateMachine");
  7. }
  8. ExecutionContextSwitcher executionContextSwitcher = default(ExecutionContextSwitcher);
  9. RuntimeHelpers.PrepareConstrainedRegions();
  10. try
  11. {
  12. ExecutionContext.EstablishCopyOnWriteScope(ref executionContextSwitcher);
  13. stateMachine.MoveNext();
  14. }
  15. finally
  16. {
  17. executionContextSwitcher.Undo();
  18. }
  19. }

剖析MoveNext异步编程:async/await深入理解 - 图3对比IDE中的代码, 如下所示:
异步编程:async/await深入理解 - 图4

总结

await等待的是任务的操作值, 最终返回是异步操作的返回结果。而这一切都是因为编译器创建了一系列复杂的状态机制, 以达到其实现。
原文地址:https://www.cnblogs.com/zh7791/p/9951478.html
https://www.cnblogs.com/bile/p/9470025.html
“ 总之async 不加awit就是异步,加了就同步处理”
这句话说的不是不对,是没说全。

整个async方法都是异步的,只有被await修饰的Task做同步处理;
但await后面的业务,会等这个Task结束再进入异步。

await只关注它所修饰的Task是否完成,不会影响它之前的业务;
就是说并不是把方法一分为二,await前面的同步后面的异步。