比较SynchronizationContext

如何比较SynchronizationContext? 在使用BeginInvoke时,似乎同一个Dispatcher可以创建不同的SynchronizationContext。 当我深入研究两个(不相等的)上下文时,我看到调度程序的线程ID是相同的,但它们彼此不相等。

public partial class MainWindow : Window { private SynchronizationContext contexta; private SynchronizationContext contextb; private SynchronizationContext contextc; private SynchronizationContext contextd; public MainWindow() { InitializeComponent(); contexta = SynchronizationContext.Current; Loaded += MainWindow_Loaded; } private void MainWindow_Loaded(object sender, RoutedEventArgs e) { contextb = SynchronizationContext.Current; Dispatcher.Invoke(() => { contextc = SynchronizationContext.Current; }); Dispatcher.BeginInvoke(new Action(() => { contextd = SynchronizationContext.Current; })); Debug.Assert(contexta != contextb); Debug.Assert(contexta == contextc); // fails... why?!?!? Debug.Assert(contexta == contextd); // fails... why?!?!? Debug.Assert(contextc == contextd); // fails... why?!?!? } 

也许这两个人不能一起使用。 我注意到这实际上有效:

  contexta.Send(new SendOrPostCallback((s) => { contexte = SynchronizationContext.Current; }), null); 

更新但奇怪的是,它并不总是有效。

  public override void AddRange(IEnumerable items) { if (SynchronizationContext.Current == _context) { base.AddRange(items); } else { _context.Send(new SendOrPostCallback((state) => { AddRange(state as IEnumerable); }), items); } } 

永远不会得到匹配的_context并继续下去,例如。 即使它不应该。 后一个例子,线程实际上最终是相同的,并且有一个上下文,但它是不同的。

Update2好的,我让它工作了,但我真的觉得不舒服。 显然,当你发布或发送时,你的任务是从正确的线程运行的,但如果你不是来自UI,似乎会生成一个新的SynchronizationContext。

  public override void AddRange(IEnumerable items) { if (SynchronizationContext.Current == _context) { base.AddRange(items); } else { _context.Post(new SendOrPostCallback((state) => { if (SynchronizationContext.Current != _context) SynchronizationContext.SetSynchronizationContext(_context); // called every time.. strange AddRange(items); }), null); } } 

看看这个:

在此处输入图像描述

“需要对直接调用者完全信任。此成员不能被部分受信任或透明的代码使用。” 🙁

我想您对BaseCompatibilityPreferences.ReuseDispatcherSynchronizationContextInstance感兴趣。

此设置指示是否将单个SynchronizationContext实例用于给定的Dispatcher对象。 默认情况下,直到.net 4,并且在.net 4.5中为false(这是LukeN观察到的行为更改)。

现在,如果你的目标只是直接打电话而不是打电话.Send()我会说:

  1. 当调用.Send()时,DispatcherSynchronizationContext实际上只是直接调用,如果你在正确的线程上(不使用调度程序队列),所以你无论如何都没有获得太多(一些检查和来自额外层的调用间接)。

  2. 如果您只对WPF进行编码,则可以使用Dispatcher.CheckAccess()和Dispatcher.Invoke()来执行您想要的操作。

在一般情况下,没有办法“比较”两个SynchronizationContexts,所以你应该只调用.Send()。 无论如何,它不太可能成为性能问题,请记住,过早优化是所有邪恶的根源 – >先测量。