简体   繁体   中英

SynchronizationContext Send() supposed to be the same thread?

I have this scenario where I try to handle an event on the same thread as it was created. Which is commonly done in the UiThread, but I'm not on the UiThread to start with. I have some test with basically the following steps. I have left out some details. I am not really sure whether or not this should act as I think it should .

First I check the Id of the current thread

var myThreadId = Thread.CurrentThread.ManagedThreadId;

I create a SynchronizationContext and set is as current

var _context = new SynchronizationContext();
SynchronizationContext.SetSynchronizationContext(_context);

Then I send some action to the context (We are now on another thread)

_context.Send(x => _action(sender, e), null);

Inside this action, I check the ThreadId again

Assert.Equal(myThreadId, Thread.CurrentThread.ManagedThreadId);

This fails. Am I not supposed to be on my original thread again?

If you create a new SynchronizationContext , it will always wrap the Thread Pool and never execute Send or Post on the UI thread.

From MSDN ;

The SynchronizationContext class is a base class that provides a free-threaded context with no synchronization.

For example;

void Button_Click(object sender, EventArgs e)
{
     var context = SynchronizationContext.Current;

     // this is executred on the UI thread.
     context.Send(() =>
     {
           // this is also executed on the UI thread.
     });

     Task.Run(() =>
     {
         // this is executed on a worker thread
         context.Send(() =>
         {
             // this is still executed on the UI thread!
         });
     }

     // what you are doing will always execute on a worker thread.
     var  myNewContext = new SynchronizationContext();
     SynchronizationContext.SetSynchronizationContext(myNewContext);

     myNewContext.Send(() =>
     {
         // this will run on a worker thread.
     }         
}

Further Reading

Parallel Computing - It's All About the SynchronizationContext

Creating a new SynchronizationContext and using Send or Post is exactly the same as a synchronous delegate invocation as if you'd do it yourself. The code is rather simple (taken from the source ):

public virtual void Send(SendOrPostCallback d, Object state)
{
    d(state);
}

You're trying to mimic the operation of custom contexts, such as the DispatcherSynchronizationContext for example, which is aware of the WPF's UI message loop thread. That behavior does not happen here.

If you're coming from the UI thread, you'll need to capture the context and pass it along.

You can see this more clearly inside the DispatcherSynchronizationContext which queues work to the UI using the Dispatcher class:

/// <summary>
///     Synchronously invoke the callback in the SynchronizationContext.
/// </summary>
public override void Send(SendOrPostCallback d, Object state)
{
    // Call the Invoke overload that preserves the behavior of passing
    // exceptions to Dispatcher.UnhandledException.  
    if(BaseCompatibilityPreferences.GetInlineDispatcherSynchronizationContextSend() && 
       _dispatcher.CheckAccess())
    {
        // Same-thread, use send priority to avoid any reentrancy.
        _dispatcher.Invoke(DispatcherPriority.Send, d, state);
    }
    else
    {
        // Cross-thread, use the cached priority.
        _dispatcher.Invoke(_priority, d, state);
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM