Jam*_* Ko 6 .net c# asynchronous task async-await
尽管已经编写了多年的C#,但我不是async的专家,但是在阅读了一些MSDN博客文章之后,AFAICT:
Task
)可以捕获或不捕获当前SynchronizationContext
.SynchronizationContext
大致相当于一个线程:如果我在UI线程和呼叫上await task
,这"流动语境",延续在UI线程上运行.如果我调用await task.ConfigureAwait(false)
,则继续在一些随机线程池线程上运行,该线程可能是/可能不是UI线程.OnCompleted
流上下文,UnsafeOnCompleted
并不流动上下文.好了,有了这个,我们来看看Roslyn生成的代码await Task.Yield()
.这个:
using System;
using System.Threading.Tasks;
public class C {
public async void M() {
await Task.Yield();
}
}
Run Code Online (Sandbox Code Playgroud)
这个编译器生成的代码的结果(你可以在这里验证自己):
public class C
{
[CompilerGenerated]
[StructLayout(LayoutKind.Auto)]
private struct <M>d__0 : IAsyncStateMachine
{
public int <>1__state;
public AsyncVoidMethodBuilder <>t__builder;
private YieldAwaitable.YieldAwaiter <>u__1;
void IAsyncStateMachine.MoveNext()
{
int num = this.<>1__state;
try
{
YieldAwaitable.YieldAwaiter yieldAwaiter;
if (num != 0)
{
yieldAwaiter = Task.Yield().GetAwaiter();
if (!yieldAwaiter.IsCompleted)
{
num = (this.<>1__state = 0);
this.<>u__1 = yieldAwaiter;
this.<>t__builder.AwaitUnsafeOnCompleted<YieldAwaitable.YieldAwaiter, C.<M>d__0>(ref yieldAwaiter, ref this);
return;
}
}
else
{
yieldAwaiter = this.<>u__1;
this.<>u__1 = default(YieldAwaitable.YieldAwaiter);
num = (this.<>1__state = -1);
}
yieldAwaiter.GetResult();
yieldAwaiter = default(YieldAwaitable.YieldAwaiter);
}
catch (Exception arg_6E_0)
{
Exception exception = arg_6E_0;
this.<>1__state = -2;
this.<>t__builder.SetException(exception);
return;
}
this.<>1__state = -2;
this.<>t__builder.SetResult();
}
[DebuggerHidden]
void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine)
{
this.<>t__builder.SetStateMachine(stateMachine);
}
}
[AsyncStateMachine(typeof(C.<M>d__0))]
public void M()
{
C.<M>d__0 <M>d__;
<M>d__.<>t__builder = AsyncVoidMethodBuilder.Create();
<M>d__.<>1__state = -1;
AsyncVoidMethodBuilder <>t__builder = <M>d__.<>t__builder;
<>t__builder.Start<C.<M>d__0>(ref <M>d__);
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,AwaitUnsafeOnCompleted
正在使用awaiter调用,而不是AwaitOnCompleted
.AwaitUnsafeOnCompleted
反过来,呼叫UnsafeOnCompleted
等待者.YieldAwaiter
不会流入当前的上下文UnsafeOnCompleted
.
这真让我感到困惑,因为这个问题似乎意味着它Task.Yield
确实捕获了当前的背景; 提问者很沮丧,因为缺乏一个没有的版本.所以我很困惑:是否Yield
捕获当前上下文?
如果没有,我怎么强迫它?我在UI线程上调用这个方法,我真的需要继续在UI线程上运行.YieldAwaitable
缺乏ConfigureAwait()
方法,所以我不能写await Task.Yield().ConfigureAwait(true)
.
谢谢!
正如评论中所指出的,回答问题的一个简单方法就是运行代码并查看会发生什么.您会发现在原始上下文中恢复执行.
我觉得你被红鲱鱼分心了.是的,AwaitUnsafeOnCompleted()
调用UnsafeOnCompleted()
,进而通过false
对flowContext
参数的QueueContinuation()
方法.但是,这一切忽视的事实AsyncMethodBuilderCore
使用的对象创建的延续Action
,创建Action
通过捕获背景下,这样的延续,可以在原来的上下文中执行.
AsyncVoidMethodBuilder
状态机中使用的是什么(至少就你的问题而言)并不重要,因为创建的延续本身会处理回到原始上下文.
事实上,这是一个核心特征await
.如果默认情况下,某些await
语句在捕获的上下文中继续,而其他语句则没有,那么API将会被打破.一个主要原因async
/ await
如此强大,它不仅允许编写以线性,同步出现的方式使用异步操作的代码,它基本上消除了我们曾经试图回到特定上下文的所有麻烦(例如UI线程或ASP.NET上下文)完成一些异步操作.
TheExecutionContext
与await
(通常是 a SynchronizationContext
)捕获的上下文不同。
总而言之,ExecutionContext
必须始终为开发人员代码流;否则是一个安全问题。在某些情况下(例如,在编译器生成的代码中),编译器知道不流动是安全的(即,它将通过另一种机制流动)。正如 Peter 所追踪的,这基本上就是这种情况下发生的事情。
但是,这与由await
(当前SynchronizationContext
或TaskScheduler
)捕获的上下文没有任何关系。看一看中的逻辑YieldAwaiter.QueueContinuation
:如果有当前的SynchronizationContext
or TaskScheduler
,它总是被使用,flowContext
参数被忽略。这是因为该flowContext
参数仅指的是流动的ExecutionContext
而不是SynchronizationContext
/ TaskScheduler
。
相比之下,任务等待者结束于Task.SetContinuationForAwait
,它有两个bool
参数:continueOnCapturedContext
用于确定是否捕获await
上下文(SynchronizationContext
或TaskScheduler
),以及flowExecutionContext
用于确定是否有必要将ExecutionContext
.