使用Visual Studio 2015 Update 3和针对.NET 4.6.1的C#测试项目,我得到以下行为:
[TestClass]
public class AwaitTests
{
[TestMethod]
public void AsyncRemovingSyncContext_PartialFail()
{
Log("1");
SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
Log("2");
HasAwait().Wait(); // The continuation in this method is on wrong thread
Log("5");
Assert.IsNotNull(SynchronizationContext.Current);
}
[TestMethod]
public async Task AsyncRemovingSyncContext_Fail()
{
Log("1");
SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
Log("2");
await HasAwait();
Log("5"); // Issue is here - Sync Context is now null
Assert.IsNotNull(SynchronizationContext.Current);
}
public async Task HasAwait()
{
Log("3");
await Task.Delay(300);
Log("4");
}
private void Log(string text)
{
Console.WriteLine($"{text} - Thread {System.Threading.Thread.CurrentThread.ManagedThreadId} - {SynchronizationContext.Current}");
}
}
这是输出:
AsyncRemovingSyncContext_PartialFail
1-线程7-
2-线程7-System.Threading.SynchronizationContext
3-线程7-System.Threading.SynchronizationContext
4-线8-
5-线程7-System.Threading.SynchronizationContext
AsyncRemovingSyncContext_Error
1-线程7-
2-线程7-System.Threading.SynchronizationContext
3-线程7-System.Threading.SynchronizationContext
4-线8-
5-线8-
-声明抛出异常-
我已经做过其他测试,到目前为止,该方法中
await
关键字的存在与Sync Context的清除之间存在100%的相关性。这包括异步lambda。这对我很重要,因为似乎一旦遇到
await
,就会删除同步上下文。这意味着,如果我以相同的方法执行两个await
,则第二个继续操作将仅在线程池中运行(没有Sync Context时的默认行为)。这是框架/编译器错误,还是我做错了什么?
具体而言,由于我确定有人会以一种或另一种形式询问,所以我有一个Active Object,我想为其启用
async
\await
支持,但是只有在我可以确保将继续发送到的情况下,我才能这样做。我的ActiveObjectSynchronizationContext
,目前不是因为它已被清除。我已经看过this question(与UI上下文4.0错误类似),但这无关紧要,因为我正在运行4.6.1,并且正在使用非UI线程。
我还遵循了this other question的建议,并确保我的Sync Context实现了
CreateCopy
,但是从测试配置文件中我可以知道甚至没有调用该方法。 最佳答案
new SynchronizationContext()
by convention与null
同步上下文相同;即线程池上下文。因此,这些测试中的行为并不意外。await
行为正确;它正在查看当前的同步上下文,并使用Post
继续到该上下文。但是,线程池同步上下文仅在线程池线程上执行委托(delegate)-不会将SynchronizationContext.Current
设置为new SynchronizationContext
,因为约定是null
是线程池同步上下文。
我相信您的真实代码存在的问题是,在执行排队的委托(delegate)时,您的ActiveObjectSynchronizationContext
没有将自身设置为当前代码。 SynchronizationContext.Post
的职责是在执行委托(delegate)之前在必要时调用SetSynchronizationContext
,尽管-如果我错了,请更正我-名称“Active Object”似乎暗示着一个类似于单线程STA的模型。如果是这种情况,则无需设置Post
;它只需要在正确的线程上执行委托(delegate),该线程应该已经具有正确的当前同步上下文。
如果有帮助,我可以使用单线程SynchronizationContext
here,尽管它不进行任何(显式)消息泵送。