比较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()我会说:
-
当调用.Send()时,DispatcherSynchronizationContext实际上只是直接调用,如果你在正确的线程上(不使用调度程序队列),所以你无论如何都没有获得太多(一些检查和来自额外层的调用间接)。
-
如果您只对WPF进行编码,则可以使用Dispatcher.CheckAccess()和Dispatcher.Invoke()来执行您想要的操作。
在一般情况下,没有办法“比较”两个SynchronizationContexts,所以你应该只调用.Send()。 无论如何,它不太可能成为性能问题,请记住,过早优化是所有邪恶的根源 – >先测量。