如何比较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<T> items)
{
if (SynchronizationContext.Current == _context)
{
base.AddRange(items);
}
else
{
_context.Send(new SendOrPostCallback((state) =>
{
AddRange(state as IEnumerable<T>);
}), items);
}
}
例如,永远不会得到匹配的_context并继续下去。即使它不应该。后一个例子,线程实际上最终是相同的,并且有一个上下文,但它是不同的。
Update2 好的,我让它上班了,但我真的觉得不舒服。显然,当你发布或发送时,你的任务是从正确的线程运行的,但如果你不是来自UI,似乎会生成一个新的SynchronizationContext。
public override void AddRange(IEnumerable<T> 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);
}
}
看看这个:
“需要对直接调用者完全信任。此成员不能被部分受信任或透明的代码使用。” :(
答案 0 :(得分:2)
我觉得你很感兴趣 BaseCompatibilityPreferences.ReuseDispatcherSynchronizationContextInstance
此设置指示是否将单个SynchronizationContext实例用于给定的Dispatcher对象。默认情况下,直到.net 4,并且在.net 4.5中为false(这是LukeN观察到的行为更改)。
现在,如果你的目标只是直接打电话而不是打电话。发送()我会说:
当调用.Send()时,如果你在正确的线程上(不使用调度程序队列),DispatcherSynchronizationContext实际上只是直接调用,所以你无论如何都没有获得太多的收益(一些检查和从额外的间接层调用。)
如果您只对WPF进行编码,则可以使用Dispatcher.CheckAccess()和Dispatcher.Invoke()来执行您想要的操作。
在一般情况下,没有办法“比较”两个SynchronizationContexts,所以你应该只调用.Send()。无论如何,它不太可能成为性能问题,请记住,过早优化是所有邪恶的根源 - &gt;先测量。