tof*_*tim 9 c# 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?!?!?
}
Run Code Online (Sandbox Code Playgroud)
也许这两个人不能一起使用.我注意到这实际上有效:
contexta.Send(new SendOrPostCallback((s) =>
{
contexte = SynchronizationContext.Current;
}), null);
Run Code Online (Sandbox Code Playgroud)
更新但奇怪的是,它并不总是有效.
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);
}
}
Run Code Online (Sandbox Code Playgroud)
例如,永远不会得到匹配的_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);
}
}
Run Code Online (Sandbox Code Playgroud)
看看这个:

"需要完全信任直接调用者.此成员不能被部分受信任或透明的代码使用." :(
我认为您对BaseCompatibilityPreferences.ReuseDispatcherSynchronizationContextInstance感兴趣 。
此设置指示单个 SynchronizationContext 实例是否用于给定的 Dispatcher 对象。在 .net 4 之前默认为 true,在 .net 4.5 中默认为 false(这是 LukeN 观察到的行为变化)。
现在,如果您的目标只是直接调用而不是调用 .Send() 我会说:
当调用 .Send() 时,如果您位于正确的线程上(不使用调度程序队列),DispatcherSynchronizationContext 实际上只是直接调用,因此无论如何您都不会获得太多(来自额外层的一些检查和调用)间接)。
如果您只针对 WPF 进行编码,则可以使用 Dispatcher.CheckAccess() 和 Dispatcher.Invoke() 来执行您想要的操作。
在一般情况下,无法“比较”两个 SynchronizationContext,因此您应该只调用 .Send()。无论如何,这不太可能是性能问题,请记住,过早的优化是万恶之源 -> 首先衡量。