Compiler should be able to find an instance or an extension method called GetAwaiter. The return type of this method should follow certain requirements:
Start 1 Before Await 1 ShowDialogA 1 OnCompleted 1 at System.Environment.get_StackTrace() at ShowDialogAwaitable.OnCompleted(Action continuation) in [Omitted Path]\Program.cs:line 64 at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AwaitOnCompleted[TAwaiter,TStateMachine](TAwaiter& awaiter, TStateMachine& stateMachine, Task`1& taskField) at Program.<<Main>$>g__Func|0_0() in [Omitted Path]\Program.cs:line 10 at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine) at Program.<<Main>$>g__Func|0_0() at Program.<Main>$(String[] args) in [Omitted Path]\Program.cs:line 15 After Sleep 1 After Await Value = 5 1 at System.Environment.get_StackTrace() at Program.<<Main>$>g__Func|0_0() in [Omitted Path]\Program.cs:line 11 at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AsyncStateMachineBox`1.MoveNext(Thread threadPoolThread) at ShowDialogAwaitable.OnDialogClosed(Int32 a) in [Omitted Path]\Program.cs:line 54 at DialogManager.Update() in [Omitted Path]\Program.cs:line 30 at Program.<Main>$(String[] args) in [Omitted Path]\Program.cs:line 18
After Sleep 1 After Await Bar Value = 5 1 at System.Environment.get_StackTrace() at Program.<<Main>$>g__Func|0_1() in [Omitted Path]\Program.cs:line 15 at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AsyncStateMachineBox`1.MoveNext(Thread threadPoolThread) at System.Threading.Tasks.AwaitTaskContinuation.RunOrScheduleAction(IAsyncStateMachineBox box, Boolean allowInlining) at System.Threading.Tasks.Task.RunContinuations(Object continuationObject) at System.Threading.Tasks.Task`1.TrySetResult(TResult result) at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.SetExistingTaskResult(Task`1 task, TResult result) at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.SetResult(TResult result) at Program.<<Main>$>g__Bar|0_0() in [Omitted Path]\Program.cs:line 10 at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AsyncStateMachineBox`1.MoveNext(Thread threadPoolThread) at ShowDialogAwaitable.OnDialogClosed(Int32 a) in [Omitted Path]\Program.cs:line 146 at DialogManager.Update() in [Omitted Path]\Program.cs:line 34 at Program.<Main>$(String[] args) in [Omitted Path]\Program.cs:line 22
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AsyncStateMachineBox`1.MoveNext(Thread threadPoolThread) at System.Threading.Tasks.AwaitTaskContinuation.RunOrScheduleAction(IAsyncStateMachineBox box, Boolean allowInlining) at System.Threading.Tasks.Task.RunContinuations(Object continuationObject) at System.Threading.Tasks.Task`1.TrySetResult(TResult result) at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.SetExistingTaskResult(Task`1 task, TResult result) at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.SetResult(TResult result)
Before Await 1 ShowDialogA 1 OnCompleted 1 at System.Environment.get_StackTrace() at PlayGround.ShowDialogAwaitable.OnCompleted(Action continuation) in [Omitted Path]\Awaitable.cs:line 48 at PlayGround.MiniTaskBuilder`1.AwaitOnCompleted[TAwaiter,TStateMachine](TAwaiter& awaiter, TStateMachine& stateMachine) in [Omitted Path]\MIniTask.cs:line 35 at Program.<<Main>$>g__Bar|0_0() in [Omitted Path]\Program.cs:line 64 at PlayGround.MiniTaskBuilder`1.Start[TStateMachine](TStateMachine& stateMachine) in [Omitted Path]\MIniTask.cs:line 21 at Program.<<Main>$>g__Bar|0_0() at Program.<<Main>$>g__Func|0_1() in [Omitted Path]\Program.cs:line 69 at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine) at Program.<<Main>$>g__Func|0_1() at Program.<Main>$(String[] args) in [Omitted Path]\Program.cs:line 73 After Await Bar Value = 5 1 at System.Environment.get_StackTrace() at Program.<<Main>$>g__Func|0_1() in [Omitted Path]\Program.cs:line 70 at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AsyncStateMachineBox`1.MoveNext(Thread threadPoolThread) at PlayGround.MiniTask`1.SetResult(T result) in [Omitted Path]\MIniTask.cs:line 62 at PlayGround.MiniTaskBuilder`1.SetResult(T result) in [Omitted Path]\MIniTask.cs:line 16 at Program.<<Main>$>g__Bar|0_0() in [Omitted Path]\Program.cs:line 65 at PlayGround.ShowDialogAwaitable.OnDialogClosed(Int32 a) in [Omitted Path]\Awaitable.cs:line 38 at PlayGround.DialogManager.Update() in [Omitted Path]\Awaitable.cs:line 15 at Program.<Main>$(String[] args) in [Omitted Path]\Program.cs:line 75
Process finished with exit code 0.
哈,StackTrace看起来短多了。
关于ICriticalNotifyCompletion与INotifyCompletion的区别
考虑如下代码
1 2 3 4 5
AsyncLocal<int> V = new AsyncLocal<int>(); V.Value = 1; var awaiter = Task.Yield().GetAwaiter(); awaiter.UnsafeOnCompleted(() => Console.WriteLine($"{V.Value}!")); Thread.Sleep(1000);
var context = new MySynchronizationContext(); SynchronizationContext.SetSynchronizationContext(context); asyncvoidFoo() { Console.WriteLine($"Before {Environment.CurrentManagedThreadId}"); await Task.Delay(100); Console.WriteLine($"After {Environment.CurrentManagedThreadId}{Environment.StackTrace}"); }
Foo(); while (true) { context.Update(); Thread.Sleep(100); }
输出为:
1 2 3 4 5 6 7
Before 1 After 1 at System.Environment.get_StackTrace() at Program.<<Main>$>g__Foo|0_0() in [Omitted Path]\Program.cs:line 68 at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AsyncStateMachineBox`1.MoveNext(Thread threadPoolThread) at PlayGround.MySynchronizationContext.Update() in [Omitted Path]\MySynchronizationContext.cs:line 18 at Program.<Main>$(String[] args) in [Omitted Path]\Program.cs:line 74
// Start is called before the first frame update voidStart() { _logic = new BattleLogic(this); _logic.Start(); }
// Update is called once per frame voidUpdate() { if (_action != null) { _action.Update(); if (_action.IsCompleted) { _action = null; } } if (_action == null) { _actions.TryDequeue(out _action); } }
publicenum Command { Attack, Skill, Escape }
publicenum Skill { FireBall, Heal }
private SelectActionAwaitable<Command> SelectCommand() { var a = new SelectActionAwaitable<Command>("Select Command, 1. Attack, 2. Skill, 3. Escape", 3, _text); _actions.Enqueue(a); return a; } private SelectActionAwaitable<Skill> SelectSkill() { var a = new SelectActionAwaitable<Skill>("Select Skill, 1. FireBall, 2. Heal", 2, _text); _actions.Enqueue(a); return a; }
private DisplayMessageAwaitable DisplayMessage(string msg) { var a = new DisplayMessageAwaitable(msg, _text); _actions.Enqueue(a); return a; }