Regarding how Async and Await works c#
我在这个站点上看到了一些关于异步和等待使用的帖子。很少有人说Async和Await在单独的后台线程上完成其任务意味着生成一个新的后台线程,很少有人说No意味着Async和Await不启动任何单独的后台线程来完成其任务。
所以任何人只要告诉我异步情况下会发生什么,并在使用异步时等待。
这是一个小节目1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | class Program { static void Main(string[] args) { TestAsyncAwaitMethods(); Console.WriteLine("Press any key to exit..."); Console.ReadLine(); } public async static void TestAsyncAwaitMethods() { await LongRunningMethod(); } public static async Task<int> LongRunningMethod() { Console.WriteLine("Starting Long Running method..."); await Task.Delay(5000); Console.WriteLine("End Long Running method..."); return 1; } } |
输出为:
1 2 3 | Starting Long Running method... Press any key to exit... End Long Running method... |
问题是
如果您使用
但是,对于IO操作,它依赖IO完成端口在操作完成时发出通知。
你们的两个陈述可能都是对的,但都令人困惑。
异步等待通常在单独的后台线程上完成,但并不意味着它启动任何单独的后台线程来完成作业。
这些异步操作的要点是在执行异步操作时不保留线程,因为真正的异步操作不需要线程。
该操作之前的部分可以是CPU绑定的,并且确实需要一个线程,它们由调用线程执行。该操作之后的部分(通常称为完成)也需要一个线程。如果有一个
因此,在您的示例中,
您的示例流程如下:主线程开始执行
5秒后,
总之,一个真正的异步操作不需要一个线程能够运行,但它在完成之后需要一个线程,这通常是来自
理解引擎盖下发生的事情的一个简单方法是使用sharplab,如果粘贴简短的示例,您将了解C编译器是如何重写包含
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 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 | using System; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Security; using System.Security.Permissions; using System.Threading.Tasks; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.0.0.0")] [module: UnverifiableCode] internal class Program { [CompilerGenerated] private sealed class <TestAsyncAwaitMethods>d__1 : IAsyncStateMachine { public int <>1__state; public AsyncVoidMethodBuilder <>t__builder; private TaskAwaiter<int> <>u__1; private void MoveNext() { int num = <>1__state; try { TaskAwaiter<int> awaiter; if (num != 0) { awaiter = LongRunningMethod().GetAwaiter(); if (!awaiter.IsCompleted) { num = (<>1__state = 0); <>u__1 = awaiter; <TestAsyncAwaitMethods>d__1 stateMachine = this; <>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine); return; } } else { awaiter = <>u__1; <>u__1 = default(TaskAwaiter<int>); num = (<>1__state = -1); } awaiter.GetResult(); } catch (Exception exception) { <>1__state = -2; <>t__builder.SetException(exception); return; } <>1__state = -2; <>t__builder.SetResult(); } 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); } } [CompilerGenerated] private sealed class <LongRunningMethod>d__2 : IAsyncStateMachine { public int <>1__state; public AsyncTaskMethodBuilder<int> <>t__builder; private TaskAwaiter <>u__1; private void MoveNext() { int num = <>1__state; int result; try { TaskAwaiter awaiter; if (num != 0) { Console.WriteLine("Starting Long Running method..."); awaiter = Task.Delay(5000).GetAwaiter(); if (!awaiter.IsCompleted) { num = (<>1__state = 0); <>u__1 = awaiter; <LongRunningMethod>d__2 stateMachine = this; <>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine); return; } } else { awaiter = <>u__1; <>u__1 = default(TaskAwaiter); num = (<>1__state = -1); } awaiter.GetResult(); Console.WriteLine("End Long Running method..."); result = 1; } catch (Exception exception) { <>1__state = -2; <>t__builder.SetException(exception); return; } <>1__state = -2; <>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); } } private static void Main(string[] args) { TestAsyncAwaitMethods(); Console.WriteLine("Press any key to exit..."); Console.ReadLine(); } [AsyncStateMachine(typeof(<TestAsyncAwaitMethods>d__1))] [DebuggerStepThrough] public static void TestAsyncAwaitMethods() { <TestAsyncAwaitMethods>d__1 stateMachine = new <TestAsyncAwaitMethods>d__1(); stateMachine.<>t__builder = AsyncVoidMethodBuilder.Create(); stateMachine.<>1__state = -1; AsyncVoidMethodBuilder <>t__builder = stateMachine.<>t__builder; <>t__builder.Start(ref stateMachine); } [AsyncStateMachine(typeof(<LongRunningMethod>d__2))] [DebuggerStepThrough] public static Task<int> LongRunningMethod() { <LongRunningMethod>d__2 stateMachine = new <LongRunningMethod>d__2(); stateMachine.<>t__builder = AsyncTaskMethodBuilder<int>.Create(); stateMachine.<>1__state = -1; AsyncTaskMethodBuilder<int> <>t__builder = stateMachine.<>t__builder; <>t__builder.Start(ref stateMachine); return stateMachine.<>t__builder.Task; } } |
正如在许多其他答案中所指出的那样(如该答案),
Task , for an async method that returns a value.Task , for an async method that performs an operation but returns no value.void , for an event handler.- Starting with C# 7.0, any type that has an accessible
GetAwaiter method. The object returned by theGetAwaiter method must implement the System.Runtime.CompilerServices.ICriticalNotifyCompletion interface.
关于最后一颗子弹,你可以在这里或那里了解更多(事实上它是基于模式的)。这也涉及到其他不在你问题范围内的微妙选择,但是你可以在这里对
重写代码的行为委托给编译器,Roslyn基本上是使用
在这两种情况下,如果您有一个包含
知道在有效的
1 2 3 4 5 6 7 8 9 | case -1: HelperMethods.Before(); this.awaiter = AsyncMethods.MethodAsync(this.Arg0, this.Arg1).GetAwaiter(); if (!this.awaiter.IsCompleted) { this.State = 0; this.Builder.AwaitUnsafeOnCompleted(ref this.awaiter, ref this); } break; |
大致可以翻译成(更多详情请参见迪信的博客):
1 2 3 4 5 6 7 | case -1: // -1 is begin. HelperMethods.Before(); // Code before 1st await. this.currentTaskToAwait = AsyncMethods.MethodAsync(this.Arg0, this.Arg1); // 1st task to await // When this.currentTaskToAwait is done, run this.MoveNext() and go to case 0. this.State = 0; this.currentTaskToAwait.ContinueWith(_ => that.MoveNext()); // Callback break; |
记住,如果您将
few people are saying that Async and Await complete its job on separate background thread means spawn a new background thread and few people are saying no means Async and Await does not start any separate background thread to complete its job.
关于您的代码,您可以跟踪使用的线程(即ID)以及它是否来自池:
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 | public static class Program { private static void DisplayCurrentThread(string prefix) { Console.WriteLine($"{prefix} - Thread Id: {Thread.CurrentThread.ManagedThreadId}"); Console.WriteLine($"{prefix} - ThreadPool: {Thread.CurrentThread.IsThreadPoolThread}"); } public static void Main(params string[] args) { DisplayCurrentThread("Main Pre"); TestAsyncAwaitMethods(); DisplayCurrentThread("Main Post"); Console.ReadLine(); } private static async void TestAsyncAwaitMethods() { DisplayCurrentThread("TestAsyncAwaitMethods Pre"); await LongRunningMethod(); DisplayCurrentThread("TestAsyncAwaitMethods Post"); } private static async Task<int> LongRunningMethod() { DisplayCurrentThread("LongRunningMethod Pre"); Console.WriteLine("Starting Long Running method..."); await Task.Delay(500); Console.WriteLine("End Long Running method..."); DisplayCurrentThread("LongRunningMethod Post"); return 1; } } |
将输出,例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | Main Pre - Thread Id: 1 Main Pre - ThreadPool: False TestAsyncAwaitMethods Pre - Thread Id: 1 TestAsyncAwaitMethods Pre - ThreadPool: False LongRunningMethod Pre - Thread Id: 1 LongRunningMethod Pre - ThreadPool: False Starting Long Running method... Main Post - Thread Id: 1 Main Post - ThreadPool: False End Long Running method... LongRunningMethod Post - Thread Id: 4 LongRunningMethod Post - ThreadPool: True TestAsyncAwaitMethods Post - Thread Id: 4 TestAsyncAwaitMethods Post - ThreadPool: True |
您可以注意到,
另外,正如i3arnon已经提到的,由于没有传递上下文,所以程序确实(重新)使用线程池中的线程在异步方法调用后继续执行。
关于这些"上下文",我建议您阅读这篇文章,文章将澄清什么是上下文,特别是
注意,我说了一个线程池线程来"恢复"而不是执行异步代码,您可以在这里找到更多关于这个的信息。
真正的异步方法的目的是避免将线程用于IO内容,这样可以帮助应用程序在有更多请求时进行扩展。通常可以使用
我建议你读一下那个问题的答案
Void-returning async methods have a specific purpose: to make asynchronous event handlers possible. It is possible to have an event handler that returns some actual type, but that doesn't work well with the language; invoking an event handler that returns a type is very awkward, and the notion of an event handler actually returning something doesn't make much sense.
Event handlers naturally return void, so async methods return void so that you can have an asynchronous event handler. However, some semantics of an async void method are subtly different than the semantics of an async Task or async Task method.
避免这种情况的一种方法是利用C 7.1特性,并期望
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 | public static class Program { private static void DisplayCurrentThread(string prefix) { Console.WriteLine($"{prefix} - Thread Id: {Thread.CurrentThread.ManagedThreadId}"); Console.WriteLine($"{prefix} - ThreadPool: {Thread.CurrentThread.IsThreadPoolThread}"); } public static async Task Main(params string[] args) { DisplayCurrentThread("Main Pre"); await TestAsyncAwaitMethods(); DisplayCurrentThread("Main Post"); Console.ReadLine(); } private static async Task TestAsyncAwaitMethods() { DisplayCurrentThread("TestAsyncAwaitMethods Pre"); await LongRunningMethod(); DisplayCurrentThread("TestAsyncAwaitMethods Post"); } private static async Task<int> LongRunningMethod() { DisplayCurrentThread("LongRunningMethod Pre"); Console.WriteLine("Starting Long Running method..."); await Task.Delay(500); Console.WriteLine("End Long Running method..."); DisplayCurrentThread("LongRunningMethod Post"); return 1; } } |
然后你就会得到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | Main Pre - Thread Id: 1 Main Pre - ThreadPool: False TestAsyncAwaitMethods Pre - Thread Id: 1 TestAsyncAwaitMethods Pre - ThreadPool: False LongRunningMethod Pre - Thread Id: 1 LongRunningMethod Pre - ThreadPool: False Starting Long Running method... End Long Running method... LongRunningMethod Post - Thread Id: 4 LongRunningMethod Post - ThreadPool: True TestAsyncAwaitMethods Post - Thread Id: 4 TestAsyncAwaitMethods Post - ThreadPool: True Main Post - Thread Id: 4 Main Post - ThreadPool: True |
这看起来更像你通常期望的。
更多关于
- 迪欣博客:了解C
async /await 汇编 - 迪欣博客:了解C
async /await (2)等待者模式 - 迪欣的博客:理解c
async /await (3)运行时上下文 - 史蒂芬·克利里:
async 和await 。 - 史蒂芬·克利里:没有线索
- 史蒂芬·图布:以东x1〔40〕对以东x1〔24〕。
你问错了问题
实际上你在问,包裹是怎么到我家门口的?坐船还是坐飞机?
关键是你的门阶不在乎包裹是海运还是空运。
然而,微软开发task/async/await框架的主要原因是利用基于事件的编程而不是基于线程的编程。
一般来说,基于事件的编程比基于线程的编程更高效、更快。这就是大多数.NET API使用它的原因。然而,到目前为止,大多数人都避免了基于事件的编程,因为这是非常难以理解的(同样,为了使这一过程简单化,还设置了async/wait)。
需要了解两件事:a)异步/等待使用任务(任务使用线程池)b)异步/等待不用于并行工作。
只需编译这个并查看ID:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | static void Main(string[] args) { Console.WriteLine("Id main thread is: {0}", Thread.CurrentThread.ManagedThreadId); TestAsyncAwaitMethods(); Console.WriteLine("Press any key to exit..."); Console.ReadLine(); } public async static void TestAsyncAwaitMethods() { Console.WriteLine("Id thread (void - 0) is: {0}", Thread.CurrentThread.ManagedThreadId); var _value = await LongRunningMethod(); Console.WriteLine("Id thread (void - 1) is: {0}", Thread.CurrentThread.ManagedThreadId); } public static async Task<int> LongRunningMethod() { Console.WriteLine("Id thread (int) is: {0}", Thread.CurrentThread.ManagedThreadId); Console.WriteLine("Starting Long Running method..."); await Task.Delay(1000); Console.WriteLine("End Long Running method..."); return 1; } |
只能在标记为Async的方法内部调用Await。一旦等待一个函数,框架就知道如何记住当前的调用环境,并在等待的函数完成后将控制权返回给它。
您只能等待返回任务的函数。所以所有的等待处理都是返回的任务对象(在返回任务之前,等待的方法是同步执行的)
为了给您提供一个任务,您正在等待的方法可以产生一个新的线程来完成它的工作,它可以同步返回一个已完成的任务和一个值(从一个结果创建一个任务),它可以做它想要做的任何事情。在从awaitable方法接收到的任务对象完成之前,所有await都会将控件返回给函数的父级。此时,它将从等待行继续执行您的方法。