了解幕后情况的一种简单方法是使用SharpLab,如果您粘贴简短示例,您将了解 C# 编译器如何重写包含async / await 的代码:
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;
}
}
正如许多其他关于 SO 的答案(如 that one)所指出的,async / await 将代码重写为状态机,就像 yield 语句一样,其方法返回 IEnumerator, IEnumerable、IEnumerator<T>、IEnumerable<T>。除了async方法,可以返回either:
关于最后一个项目符号,您可以阅读更多信息(事实上它是基于模式的)here 和 there。这也涉及到您的问题范围之外的其他微妙选择,但您可以有一个简短的解释here about ValueTask<TResult>, IValueTaskSource<TResult>, etc.
重写代码的行为委托给编译器,Roslyn 基本上是使用AsyncRewriter 类来知道如何重写不同的执行路径,分支以获得等效的代码。
在这两种情况下,如果您有一个包含 yield 或 async 关键字的有效代码,您就有一个初始状态,并且取决于分支、执行路径,在幕后发生的 MoveNext() 调用将从一个状态移动到另一个。
知道在有效async的情况下,下面的这种sn-p代码:
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;
大致可以翻译成(详见迪信的博客):
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;
请记住,如果您将void 作为async 方法的返回类型,您将不会有太多currentTaskToAwait =]
很少有人说 Async 和 Await 在单独的后台线程上完成其工作意味着产生一个新的后台线程,而很少有人说不意味着 Async 和 Await 不会启动任何单独的后台线程来完成其工作。
关于您的代码,您可以跟踪使用了哪个线程(即 id)以及它是否来自池:
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;
}
}
将输出例如:
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
您可以注意到LongRunningMethod 方法在Main 方法之后终止,这是因为您使用void 作为异步方法的返回类型。 async void 方法只能用于事件处理程序,不能用于其他任何事情(请参阅Async/Await - Best Practices in Asynchronous Programming)
另外,正如 i3arnon 已经提到的,由于没有传递上下文,是的,程序确实(重新)使用线程池中的线程来在异步方法调用之后恢复其执行。
关于那些“上下文”,我建议你阅读that article,这篇文章将阐明什么是上下文,特别是SynchronizationContext。
请注意,我所说的线程池线程“恢复”而不是执行异步代码,您可以了解有关此here 的更多信息。
async 方法通常被设计为利用底层调用固有的延迟,通常是 IO,例如。写入、读取磁盘上的内容、通过网络查询内容等等。
真正异步方法的目的是避免将线程用于 IO 内容,这可以帮助应用程序在您有更多请求时进行扩展。通常可以使用 async 资源在 ASP.NET WebAPI 中处理更多请求,因为它们中的每一个都会“释放”请求的线程,只要它们将访问数据库或您在该资源中进行的任何 async 调用。
我建议你阅读question的答案
返回无效的异步方法有一个特定的目的:使异步事件处理程序成为可能。有可能有一个返回一些实际类型的事件处理程序,但这不适用于该语言;调用返回类型的事件处理程序非常尴尬,并且事件处理程序实际上返回某些东西的概念没有多大意义。
事件处理程序自然返回 void,因此异步方法返回 void,以便您可以拥有异步事件处理程序。但是,async void 方法的某些语义与 async Task 或 async Task 方法的语义略有不同。
避免这种情况的一种方法是利用C# 7.1 feature 并期望Task 作为返回类型而不是void:
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;
}
}
你会得到
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
这看起来更像您通常所期望的。
更多关于async/await的资源: