9

同期コンテキストを比較するにはどうすればよいですか?BeginInvokeを使用すると、同じディスパッチャが異なるSynchronizationContextを作成できるようです。2つの(等しくない)コンテキストにドリルダウンすると、ディスパッチャーのスレッド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?!?!?
    }        

たぶん、それらの2つを一緒に使用することはできません。私はこれが実際に機能することに気づきました:

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

そしてこれを見てください:

ここに画像の説明を入力してください

「直接の発信者には完全な信頼が必要です。このメンバーは、部分的に信頼されているコードや透過的なコードでは使用できません。」:(

4

1 に答える 1

2

BaseCompatibilityPreferences.ReuseDispatcherSynchronizationContextInstanceに興味があると思います 。

この設定は、特定の Dispatcher オブジェクトに対して単一の SynchronizationContext インスタンスを使用するかどうかを決定します。.net 4 まではデフォルトで true であり、.net 4.5 では false です (これは LukeN が観察する動作の変更です)。

.Send() を呼び出すのではなく、直接呼び出しを行うことが目標である場合、次のように言います。

  1. .Send() を呼び出すとき、DispatcherSynchronizationContext は、正しいスレッド上にいる場合 (dispatcher Queue を使用しない場合)、実際には直接呼び出しを行うだけなので、とにかく多くを得ることはありません (余分なレイヤーからのいくつかのチェックと呼び出し間接)。

  2. WPF に対してのみコーディングする場合は、Dispatcher.CheckAccess() と Dispatcher.Invoke() を使用して、必要なことを行うことができます。

一般に、2 つの SynchronizationContext を「比較」する方法はないため、.Send() を呼び出すだけです。とにかく、パフォーマンスの問題になる可能性は低いです。時期尚早の最適化はすべての悪の根源であることを忘れないでください -> 最初に測定してください。

于 2013-05-29T23:51:01.813 に答える