4

我有这种情况,我尝试在创建事件的同一线程上处理事件。这通常在 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);

这失败了。我不应该再次出现在我原来的线程上吗?

4

2 回答 2

12

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

来自 MSDN

SynchronizationContext 类是提供无同步的自由线程上下文的基类。

例如;

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.
     }         
}

延伸阅读

并行计算 - 一切都与 SynchronizationContext 有关

于 2015-08-26T10:46:08.973 回答
2

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

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

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

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

DispatcherSynchronizationContext您可以使用Dispatcher该类在 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);
    }
}
于 2015-08-26T10:56:19.500 回答