【发布时间】:2017-07-14 22:46:20
【问题描述】:
尽管我写了很多年的 C#,但我并不是异步方面的专家,但是在阅读了一些 MSDN 博客文章后,我 AFAICT:
- Awaitables(例如
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();
}
}
编译器生成的代码中的结果(您可以验证自己here):
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__);
}
}
请注意,AwaitUnsafeOnCompleted 是由 awaiter 调用的,而不是 AwaitOnCompleted。 AwaitUnsafeOnCompleted 反过来在等待者上调用 UnsafeOnCompleted。 YieldAwaiterdoes not 在UnsafeOnCompleted 中流动当前上下文。
这真的让我很困惑,因为this question 似乎暗示Task.Yield 确实捕获了当前上下文;提问者对缺少没有的版本感到沮丧。所以我很困惑:Yield 是否捕获当前上下文?
如果没有,我怎么能强迫它呢?我在 UI 线程上调用此方法,我也确实需要继续在 UI 线程上运行。 YieldAwaitable 缺少ConfigureAwait() 方法,所以我不能写await Task.Yield().ConfigureAwait(true)。
谢谢!
【问题讨论】:
-
在 UI 上下文中,取决于您真正想要实现的目标,我建议使用
Dispatcher.Yield代替:msdn.microsoft.com/en-us/library/… 就是说,我不太明白您的问题。我的意思是,问题第一部分的答案很简单:创建一个 UI 应用程序,调用await Task.Yield();,检查您是否仍在 UI 线程上,瞧 -
@KevinGosse
Dispatcher.Yield()对我不可用,我正在编写一个 Xamarin.Android 应用程序。不过,你猜对了我想要实现的目标。我试图通过在 UI 线程上 CPU 密集型工作的中间以固定间隔调用await Task.Yield()来保持 UI 响应。 -
解决方案:不要在 UI 线程上处理 CPU 密集型工作;o)
-
嗯,设置控件属性并不是真正的 CPU 密集型。你应该在codereview.stackexchange.com 上询问如何重构你的代码
-
@JamesKo:
Task.Yield可能无法让您的 UI 保持响应。我不确定 Xamarin,但我知道它不会让桌面 Windows UI 保持响应 - 你需要Task.Delay具有非零延迟。也就是说,更好的解决方案可能是虚拟化。
标签: c# .net asynchronous async-await task