W sieci jest wiele materiałów na temat konstrukcji async-away. Postanowiłem zbadać kilka ciekawostek.
Użycie async bez away
Rozważmy kod
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
using System; using System.Threading.Tasks; public class C { public async void Pierwsza() { var i = await test(); } public async void Druga() { var i = test().Result; } Task<int> test() { return Task.Run(() => { return 1; }); } } |
Metoda „Pierwsza” jest napisana zgodnie ze sztuką. Budowa metody „Druga” budzi sprzeciw kompilatora:
1 |
(8,22,8,27): Warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. |
Użyłem strony http://tryroslyn.azurewebsites.net aby obnażyć prawdę. Oto prawdziwy „kod pod spodem” (fragment)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
[AsyncStateMachine(typeof(C.<Pierwsza>d__1))] public void Pierwsza() { C.<Pierwsza>d__1 <Pierwsza>d__; <Pierwsza>d__.<>4__this = this; <Pierwsza>d__.<>t__builder = AsyncVoidMethodBuilder.Create(); <Pierwsza>d__.<>1__state = -1; AsyncVoidMethodBuilder <>t__builder = <Pierwsza>d__.<>t__builder; <>t__builder.Start<C.<Pierwsza>d__1>(ref <Pierwsza>d__); } [AsyncStateMachine(typeof(C.<Druga>d__1))] public void Druga() { C.<Druga>d__1 <Druga>d__; <Druga>d__.<>4__this = this; <Druga>d__.<>t__builder = AsyncVoidMethodBuilder.Create(); <Druga>d__.<>1__state = -1; AsyncVoidMethodBuilder <>t__builder = <Druga>d__.<>t__builder; <>t__builder.Start<C.<Druga>d__1>(ref <Druga>d__); } |
Zabawa w „znajdź różnice” skutkuje tylko tym, że różnią się nazwy metod. Różnica natomiast jest w konstrukcji maszyny stanu dla każdej z metod. W pierwszym przypadku to:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
[CompilerGenerated] [StructLayout(LayoutKind.Auto)] private struct <Pierwsza>d__1 : IAsyncStateMachine { public int <>1__state; public AsyncVoidMethodBuilder <>t__builder; public C <>4__this; private TaskAwaiter<int> <>u__1; void IAsyncStateMachine.MoveNext() { int num = this.<>1__state; try { TaskAwaiter<int> taskAwaiter; if (num != 0) { taskAwaiter = this.<>4__this.test().GetAwaiter(); if (!taskAwaiter.IsCompleted) { this.<>1__state = 0; this.<>u__1 = taskAwaiter; this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<int>, C.<Pierwsza>d__1>(ref taskAwaiter, ref this); return; } } else { taskAwaiter = this.<>u__1; this.<>u__1 = default(TaskAwaiter<int>); this.<>1__state = -1; } taskAwaiter.GetResult(); taskAwaiter = default(TaskAwaiter<int>); } catch (Exception exception) { this.<>1__state = -2; this.<>t__builder.SetException(exception); return; } this.<>1__state = -2; this.<>t__builder.SetResult(); } [DebuggerHidden] void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine) { this.<>t__builder.SetStateMachine(stateMachine); } } |
W drugim znacznie krócej:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
[CompilerGenerated] [StructLayout(LayoutKind.Auto)] private struct <Druga>d__1 : IAsyncStateMachine { public int <>1__state; public AsyncVoidMethodBuilder <>t__builder; public C <>4__this; void IAsyncStateMachine.MoveNext() { try { int arg_10_0 = this.<>4__this.test().Result; } catch (Exception exception) { this.<>1__state = -2; this.<>t__builder.SetException(exception); return; } this.<>1__state = -2; this.<>t__builder.SetResult(); } [DebuggerHidden] void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine) { this.<>t__builder.SetStateMachine(stateMachine); } } |