Why SynchronizationContext does not work properly?

淺唱寂寞╮ 提交于 2019-11-29 11:55:20

See http://www.codeproject.com/KB/threads/SynchronizationContext.aspx

There is the answer you need. You must override SynchronizationContext to make it properly handling your operations.

Read starting from:

Notice that DoWork is executed on thread 11, the same thread as Run1. Not much of a SynchronizationContext into the main thread. Why? What's going on? Well... This is the part when you realize that nothing is for free in life. Threads can't just switch contexts between them, they must have an infrastructure built-in into them in order to do so. The UI thread, for example, uses a message pump, and within its SynchronizationContext, it leverages the message pump to sync into the UI thread.

Default implementation of SynchronizationContext just executes passed delegate in the calling thread (in the thread that invokes Send/Post method not the thread that captures context). If you need some particular behavior, like thread affinity for some operations, you should implement this manually. BCL contains few out-of-box implementations for simplification of UI interoperability, like WindowsFormsSynchronizationContext or DispatcherSynchronizationContext.

Your expectation is wrong because there's no general way to "inject" a delegate into a running thread. Your "first thread" was started in the test runner, will execute one or more tests, and will then stop - there's no way to interrupt it and tell it to run CallbackInFirstThread. The SynchronizationContext class runs Post-ed delegates in the thread pool because that's about the only option it has.

Derived classes like WindowsFormsSynchronizationContext make use of the message loop in WinForms applications to pass the Post-ed delegate to the UI thread, but there's no equivalent in a test runner.

If you want to check which SynchronizationContext the code you're testing is using, you could create your own derived class that sets a flag you can check in your test. Here's an example:

public class TestSynchronizationContext : SynchronizationContext
{
    [ThreadStatic]
    private static object _CurrentPostToken;
    /// <summary>
    /// Gets the context's token, if the current thread is executing a delegate that
    /// was posted to this context; otherwise, null.
    /// </summary>
    public static object CurrentPostToken
    {
        get
        {
            return _CurrentPostToken;
        }
    }

    public object Token { get; private set; }

    /// <summary>
    /// Gets a WaitHandle that is set after the context executes a posted delegate.
    /// </summary>
    public AutoResetEvent PostHandle { get; private set; }

    public TestSynchronizationContext(object token)
    {
        Token = token;
        PostHandle = new AutoResetEvent(false);
    }

    public override void Post(SendOrPostCallback d, object state)
    {
        try
        {
            _CurrentPostToken = Token;
            // Execute the callback on this thread, so that we can reset the context
            // when it's finished.
            d(state);
        }
        finally
        {
            _CurrentPostToken = null;
        }

        // The test method will wait on this handle so that it doesn't exit before
        // the synchronization context is called.
        PostHandle.Set();
    }
}

In StartWorkInFirstThread, set the context to an instance of TestSynchronizationContext:

SynchronizationContext.SetSynchronizationContext(
        new TestSynchronizationContext(new object()));

After you call BeginInvoke, you need to wait for the Post to happen before you exit the test, so call:

((TestSynchronizationContext)SynchronizationContext.Current).PostHandle.WaitOne(1000);

In CallbackInFirstThread you can check what context is being used with something like:

Assert.IsNotNull(TestSynchronizationContext.CurrentPostToken);

The point is that there's no easy way to actually post back to the first thread, but you can check that the right context is being used so that, when your code runs in a real application, the callback will be running in the UI thread.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!