Mou*_*Mou 3 .net c# task-parallel-library async-await
我在这个网站上看到了一些关于Async和Await使用的帖子.很少有人说Async和Await在单独的后台线程上完成它的工作意味着产生一个新的后台线程,很少有人说没有意味着Async和Await没有启动任何单独的后台线程来完成它的工作.
所以任何人只要告诉我Async和Await在使用时会发生什么.
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;
}
}
Run Code Online (Sandbox Code Playgroud)
输出是:
Starting Long Running method...
Press any key to exit...
End Long Running method...
Run Code Online (Sandbox Code Playgroud)
问题是async/await关于异步,而不是线程.
如果你使用Task.Run,它确实会使用后台线程(通过线程池,通过任务并行库).
但是,对于IO操作,它依赖IO完成端口来通知操作何时完成.
唯一的保证async/await是,当一个操作完成时,它将在它开始时在那里的SynchronizationContext中返回给你的调用者.实际上,这意味着它将返回UI线程(在Windows应用程序中)或可以返回HTTP响应的线程(在ASP.NET中)
了解幕后发生的事情的一种简单方法是使用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;
}
}
Run Code Online (Sandbox Code Playgroud)
正如关于 SO 的许多其他答案所指出的(就像那个答案),async/await将代码重写为状态机,就像yield使用返回IEnumerator, IEnumerable, IEnumerator<T>,的方法的语句一样IEnumerable<T>。除了对async方法,就可以返回两种:
Task<TResult>, 用于返回值的异步方法。Task, 用于执行操作但不返回任何值的异步方法。void, 用于事件处理程序。- 从 C# 7.0 开始,任何具有可访问
GetAwaiter方法的类型。该GetAwaiter方法返回的对象必须实现System.Runtime.CompilerServices.ICriticalNotifyCompletion接口。
关于最后一个项目符号,您可以在这里和那里阅读更多关于它的信息(它是基于模式的事实)。这也涉及到其他微妙的选择,是你的问题的范围了,但你可以有一个简短的说明这里有关ValueTask<TResult>,IValueTaskSource<TResult>等等。
代码重写的行为委托给编译器,Roslyn 基本上是使用AsyncRewriter类来知道如何重写不同的执行路径,分支以获得等效的代码。
在两种情况下,如果您有包含yield或async关键字的有效代码,您就有一个初始状态,并且根据分支、执行路径,MoveNext()发生在幕后的调用将从一种状态移动到另一种状态。
知道在有效async代码的情况下,下面的这种片段:
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;
Run Code Online (Sandbox Code Playgroud)
大致可以翻译成(更多细节见狄鑫的博客):
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;
Run Code Online (Sandbox Code Playgroud)
请记住,如果你有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;
}
}
Run Code Online (Sandbox Code Playgroud)
将输出例如:
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
Run Code Online (Sandbox Code Playgroud)
您可以注意到该LongRunningMethod方法在该Main方法之后终止,这是因为您用作void异步方法的返回类型。一个async void方法应该只用于事件处理程序而不是其他任何东西(请参阅Async/Await - 异步编程中的最佳实践)
此外,正如 i3arnon 已经提到的,由于没有传递上下文,是的,程序确实(重新)使用线程池中的线程在异步方法调用后恢复其执行。
关于那些“上下文”,我建议您阅读那篇文章,该文章将阐明什么是上下文,尤其是SynchronizationContext.
请注意,我说线程池线程是“恢复”而不是执行异步代码段,您可以在此处找到更多相关信息。
async方法通常旨在利用底层调用固有的任何延迟,通常是 IO,例如。写入、读取磁盘上的内容、通过网络查询内容等等。
真正异步方法的目的是避免将线程用于 IO 内容,这可以在您有更多请求时帮助应用程序扩展。通常可以使用async资源在 ASP.NET WebAPI 中处理更多请求,因为它们中的每一个都将“释放”请求的线程,只要它们访问数据库或async您在该资源中进行的任何调用。
我建议你阅读那个问题的答案
返回空值的异步方法有一个特定的目的:使异步事件处理程序成为可能。可能有一个事件处理程序返回一些实际类型,但这在语言中不能很好地工作;调用返回类型的事件处理程序非常笨拙,并且事件处理程序实际上返回某些内容的概念没有多大意义。
事件处理程序自然返回 void,因此异步方法返回 void,以便您可以拥有异步事件处理程序。但是,async void 方法的某些语义与 async Task 或 async Task 方法的语义略有不同。
避免这种情况的一种方法是利用C# 7.1 功能并期望 aTask作为返回类型而不是void:
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
Run Code Online (Sandbox Code Playgroud)
然后你会得到
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
Run Code Online (Sandbox Code Playgroud)
这看起来更像您通常期望的那样。
关于async/ 的更多资源await:
async/ await(一)编译async/ await(二)Awaitable-Awaiter 模式async/ await(三)运行时上下文async和awaitExecutionContextvsSynchronizationContext| 归档时间: |
|
| 查看次数: |
388 次 |
| 最近记录: |