比较SynchronizationContext

时间:2012-11-21 18:46:56

标签: 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?!?!?
    }        

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

        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);
        }
    }

看看这个:

enter image description here

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

1 个答案:

答案 0 :(得分:2)

我觉得你很感兴趣 BaseCompatibilityPreferences.ReuseDispatcherSynchronizationContextInstance

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

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

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

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

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