2

SynchronizationContextWinForms と WPF の GUI スレッドに同期する手段として使用しています。最近、古いスタイルの非同期コールバックに関する問題に遭遇しました。

   private void Button_Click(object sender, RoutedEventArgs e)
    {
        uiContext = SynchronizationContext.Current;

        var cl = new TcpClient();
        cl.BeginConnect("127.0.0.1", 22222, ConnectedCallback, null);
    }
    public void ConnectedCallback(IAsyncResult result)
    {
        if (SynchronizationContext.Current != uiContext)
            uiContext.Post(x => MyUIOperation(), null);
        else
            MyUIOperation();
    }

    public void MyUIOperation()
    {
        Title = "Connected";
    }

    private SynchronizationContext uiContext;

これは例外をスローします。これはSynchronizationContext.Current、コールバック関数の がキャプチャされたものと等しいため、UI 操作がコールバックのワーカー スレッドで実行されるためです。

このまったく同じコードを WinForms で使用すると、予想どおりに機能します。

今のところ、回避策として、ManagedThreadId代わりに現在をキャプチャし、コールバックで比較しています。これを処理する正しい方法は何ですか?

アップデート:

現在、次の構成を使用している非常に古い既存のクラスを変更していることを付け加えておきます。

if (control.InvokeRequired())
    control.BeginInvoke(SomeFunction);
else
    SomeFunction();

このクラスのクライアントに大きな影響を与えることなく、WinForms の依存関係を削除しようとしています。はSomeFunction()イベントを発生させているため、単に uiContext.Send() または uiContext.Post() を呼び出すと、Post() は常に呼び出しをキューに入れ、Send() は常にブロックするため、実行順序が変更されます。

また、これは私の問題の根本を示すための非常に小さなコードです。実際には、Post() を実行する関数はメイン スレッドから呼び出すことができます。

これは.NET 4.0を対象としています

4

2 に答える 2

3

SynchronizationContext.NET 4.5 では、実際にはコールバック関数が異なり、if ステートメントが true と評価されることが判明しました。ここで説明したように、これは意図的な変更でした

   WPF 4.0 had a performance optimization where it would
     frequently reuse the same instance of the
     DispatcherSynchronizationContext when preparing the
     ExecutionContext for invoking a DispatcherOperation.  This
     had observable impacts on behavior.
     1) Some task-parallel implementations check the reference
         equality of the SynchronizationContext to determine if the
         completion can be inlined - a significant performance win.
     2) But, the ExecutionContext would flow the
         SynchronizationContext which could result in the same
         instance of the DispatcherSynchronizationContext being the
         current SynchronizationContext on two different threads.
         The continuations would then be inlined, resulting in code
         running on the wrong thread.
于 2015-12-29T14:02:17.943 に答える
2

私の場合、 ConnectedCallback 関数がメインスレッドから呼び出された場合、 MyUIOperation() 関数の呼び出しをすぐに呼び出す必要があるためです。

これは、別のスレッドから呼び出された場合の非ブロッキングではなく、UI スレッドで呼び出されたMyUIOperation()場合の呼び出しがブロッキング呼び出しになることを意味します。ConnectedCallbackこの非決定論は、今後他の問題を引き起こす可能性があります。

Send代わりに電話してください。この記事によるとSend、すでに UI スレッドにある場合、への呼び出しはデリゲートを直接呼び出すだけです。

また、代わりに行うこともできますDispatcher.Invoke()

于 2015-12-28T17:05:22.607 に答える