这是该问题的后续问题:
If async-await doesn't create any additional threads, then how does it make applications responsive?
以及此博客文章:
http://blog.stephencleary.com/2013/11/there-is-no-thread.html
关于链接问题的可接受答案。。他描述了调用await
时发生的步骤。
他在步骤3和4上写道,当发生这种情况时,当前线程返回到调用上下文,这使消息循环可用于接收其他消息,从而使应用程序做出响应。然后在第5步中,他写道,我们调用await的任务现在已完成,并且原始方法的其余部分在收到新消息以继续该方法的其余部分之后继续进行。
我不了解的是,如果没有新线程,并且原始线程正忙于调用上下文,那么该任务的执行是如何开始的?
因此,我转到了博客文章,该文章描述了整个操作实际上是如何在低得多的级别完成的(老实说,我真的不知道它们是如何工作的),然后只是一个通知它已完成。但是,我不明白为什么在执行此任务时突然可以依靠其他硬件而不是CPU来进行计算,这使得以某种方式不创建新线程成为可能。计算,那么我们不需要CPU吗?然后我们回到我写的内容,即当前线程已经在忙于调用上下文了……
答案 0 :(得分:1)
但是我不明白为什么在执行此任务时突然可以依靠其他硬件而不是CPU来进行计算,这使得不创建新线程成为可能。
因为CPU不仅是硬件设备集中并行性的来源。
线程在逻辑上与CPU相关,因此对于任何与CPU绑定的工作负载,我们要么通过创建线程来显式使用线程,要么使用诸如线程池或Task.Run
之类的更高级别的机制来隐式使用-每个应用程序都在某个默认值内启动反正线程。
但是还有另一种类型的操作-输入/输出操作,意味着使用除CPU <-> RAM以外的硬件设备,例如磁盘,网络适配器,键盘,各种外围设备等。此类设备处理传入的数据并以异步方式发出-没人知道您下次按下按键时还是从网络收到新数据。为了处理异步,这样的硬件设备能够在不占用CPU的情况下传输数据(简单地说,设备在RAM所在的地址中提供了一些地址,然后数据可以自己进行传输)。这是一个非常简单的描述,但是您可以认为大多数异步流都以它结尾。如您所见,此处不需要CPU,因此无需创建新线程。至于入站数据,该机制非常相似,唯一的区别是,一旦数据到达,它将被放入特定的RAM区域以供进一步使用。当设备完成数据转换(入站或出站)时,它将发出称为中断的特定信号,以通知CPU操作完成,并且CPU对中断做出反应,并触发通常位于硬件设备驱动程序中的特定代码执行-这样,驱动程序可以将通知发送到更高级别。中断可能来自异步设备,CPU必须暂停当前正在执行的任何当前执行,并切换到中断处理程序。当设备驱动程序正在执行中断处理程序时,它会将有关I / O完成的通知发送到更高级别的OS堆栈,最后,该通知将触发启动I / O操作的应用程序。如何完成主要取决于应用程序所运行的操作系统。对于Windows,有一种称为I/O Completion Ports的特定机制,该机制意味着使用某些线程池来处理I / O完成通知。这些通知最终从CLR发送到应用程序,并触发继续执行,最终可以在单独的线程上运行,该线程可以是I / O线程池中的线程,也可以是其他任何线程,具体取决于awaiter的实现。
对于您所引用的文章的总体思路,可以用以下措辞来表述:async/await
后面没有线程,除非您明确创建它,因为await/async
只是高级通知框架本身,可以扩展为与下面的任何异步机制一起使用。
答案 1 :(得分:1)
我不了解的是,如果没有新线程,并且原始线程正忙于调用上下文,那么该任务的执行是如何开始的?
请务必注意,有two types of tasks: what I call Delegate Tasks and Promise Tasks。委托任务是并行处理中使用的任务类型-原始的“任务并行库”用法,其中任务表示要执行的一定数量的代码。
Promise Tasks仅提供完成某事的通知(以及该操作的结果/例外)。 Promise Tasks不执行代码。它们是回调的对象表示。 async
始终使用Promise Tasks。
因此,当async
方法返回Task
时,该任务就是一个Promise Task。 It doesn't "execute", but it can "complete"。
答案 2 :(得分:-1)
我认为您需要了解使async
/ await
代码正常工作的编译器级别。
采用此方法:
public async Task<int> GetValue()
{
await Task.Delay(TimeSpan.FromSeconds(1.0));
return 42;
}
编译后,您会得到:
[AsyncStateMachine(typeof(<GetValue>d__1))]
public Task<int> GetValue()
{
<GetValue>d__1 stateMachine = default(<GetValue>d__1);
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;
}
[StructLayout(LayoutKind.Auto)]
[CompilerGenerated]
private struct <GetValue>d__1 : 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)
{
awaiter = Task.Delay(TimeSpan.FromSeconds(1.0)).GetAwaiter();
if (!awaiter.IsCompleted)
{
num = (<>1__state = 0);
<>u__1 = awaiter;
<>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
return;
}
}
else
{
awaiter = <>u__1;
<>u__1 = default(TaskAwaiter);
num = (<>1__state = -1);
}
awaiter.GetResult();
result = 42;
}
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)
{
<>t__builder.SetStateMachine(stateMachine);
}
void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine)
{
//ILSpy generated this explicit interface implementation from .override directive in SetStateMachine
this.SetStateMachine(stateMachine);
}
}
通过IAsyncStateMachine.MoveNext()
,允许调用代码在碰到await
时掉线。请注意return;
中的if (!awaiter.IsCompleted)
。
这只是一台完成所有魔力的状态机。