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.
}
}
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);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With