异步方法中的C#await清除SynchronizationContext(带有repro测试)

Mat*_*ein 1 .net c# multithreading asynchronous async-await

使用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}");
    }
}
Run Code Online (Sandbox Code Playgroud)

这是输出:

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 -
-Assert Exception Thrown-

我已经完成了其他测试,到目前为止await,方法中关键字的存在与同步上下文的清除之间存在100%的相关性.这包括异步lambda.

这对我很重要,因为看起来只要await遇到一个,就会删除同步上下文.这意味着如果我await在同一个方法中执行两个s,第二个延续将只在线程池中运行(没有同步上下文时的默认行为).

这是一个框架/编译器错误还是我做错了什么?


具体来说,既然我确定有人会以某种形式提出要求,我有一个我希望启用\ 支持的活动对象,但我只能保证,如果我能保证继续将被发送给我目前,这不是因为它被清除了.asyncawaitActiveObjectSynchronizationContext

我已经看过这个问题了(类似于UI上下文4.0的错误),但这与我没有关系,因为我正在运行4.6.1并且我正在使用非UI线程.

我也遵循了这个其他问题的建议并确保我的Sync Context实现CreateCopy,但我可以从测试分析中看出该方法甚至没有被调用.

Ste*_*ary 5

new SynchronizationContext() 按惯例null同步上下文相同; 也就是说,线程池上下文.因此,这些测试中的行为并不出乎意料.

await行为正确; 它正在查看当前的同步上下文并将Post其继续到该上下文.但是,线程池同步方面只执行一个线程池线程的委托-它不设置SynchronizationContext.Currentnew SynchronizationContext,因为该公约是null 线程池同步上下文.

我相信你的真实代码的问题是你ActiveObjectSynchronizationContext在执行排队的委托时没有将自己设置为当前.如果有必要,在执行委托之前SynchronizationContext.Post调用SetSynchronizationContext是有责任的,但是 - 如果我错了,请纠正我 - 名称"Active Object"似乎意味着单线程的STA模型.如果是这种情况,那么Post就不需要设置它; 它只需要在正确的线程上执行委托,该线程应该已经具有正确的当前同步上下文.

如果它有帮助,我在SynchronizationContext 这里有一个单线程,虽然它没有做任何类型的(显式)消息.