2

对于 WinForms 和 WPF,我将SynchronizationContext其用作与 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

事实证明,在 .NET 4.5SynchronizationContext中,回调函数实际上是不同的,并且 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 回答