SynchronizationContext Send() 应该是同一个线程?

SynchronizationContext Send() supposed to be the same thread?

我有这种情况,我尝试在创建事件的同一线程上处理事件。这通常在 UiThread 中完成,但我不是从 UiThread 开始的。我基本上按照以下步骤进行了一些测试。我遗漏了一些细节。我不太确定这是否应该像我认为的那样。

首先我查看当前线程的Id

var myThreadId = Thread.CurrentThread.ManagedThreadId;

我创建了一个 SynchronizationContext 并将其设置为当前

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

然后我向上下文发送一些动作(我们现在在另一个线程上)

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

在此操作中,我再次检查 ThreadId

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

这失败了。我不应该再回到我原来的话题吗?

如果您创建一个新的 SynchronizationContext,它将始终包装线程池并且永远不会在 UI 线程上执行 SendPost

From MSDN;

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

例如;

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

创建一个新的 SynchronizationContext 并使用 SendPost 与同步委托调用完全相同,就像您自己做一样。代码相当简单(取自the source):

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

您正在尝试模仿自定义上下文的操作,例如 DispatcherSynchronizationContext,它知道 WPF 的 UI 消息循环线程。这种行为不会发生在这里。

如果您来自 UI 线程,则需要捕获上下文并将其传递。

您可以在 DispatcherSynchronizationContext 中更清楚地看到,哪些队列使用 Dispatcher class:

为 UI 工作
/// <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);
    }
}