【问题标题】:WPF + Tasks + WCF = No SynchronizationContext?WPF + 任务 + WCF = 没有 SynchronizationContext?
【发布时间】:2011-06-29 22:47:45
【问题描述】:

我有一个 WPF 应用程序,它使用 System.Threading.Tasks 在后台调用 WCF 服务。我正在使用 Task.ContinueWith 将服务调用的结果返回给 WPF UI 线程。我的问题是,虽然延续确实在 UI 线程上运行,但当它运行时 SynchronizationContext.Current 为空。我可以运行相同的代码,在初始任务中注释掉 WCF 调用,并且继续在 UI 线程上,并按预期使用 DispatcherSynchronizationContext。

WCF 代理是使用 ChannelFactory 生成的,并使用 wsHttpBinding。没有回调合约。相关代码如下:

    private TaskScheduler _uiScheduler;

    public MainWindow()
    {
        InitializeComponent();
        _uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        var serviceTask = new Task<Int32>(ServiceCallWrapper, 
            CancellationToken.None, 
            TaskCreationOptions.None);

        var continueTask = serviceTask.ContinueWith(result => ServiceContinuation(result.Result),
                                                    CancellationToken.None,
                                                    TaskContinuationOptions.OnlyOnRanToCompletion, 
                                                    _uiScheduler);

        serviceTask.Start();
    }

    private Int32 ServiceCallWrapper()
    {
        Int32 result = 0;

        var service = {elided - initializes service using ChannelFactory };
        result = service.TheServiceMethod();
        service.Close();

        return result;
    }

    private void ServiceContinuation(Int32 result)
    { elided }

如果我按原样运行此代码,则会在正确的线程上调用 ServiceContinuation(使用 ManagedThreadID 进行验证),但 SynchronizationContext.Current 为空。如果我注释掉进行服务调用的单行 (result = service.TheServiceMethod();),则使用 DispatcherSynchronizationContext 正确调用 ServiceContinuation。

注意 - SynchronizationContext 不会永久丢失 - 如果我再次单击按钮,按钮单击处理程序确实具有正确的 SynchronizationContext。

我已经捕获了这两种情况的堆栈跟踪;他们有一些不同之处。我省略了所有相同的位,只包括它们不同的堆栈顶部,以及一些参考帧:

失败 - 调用 WCF 服务

WpfContinuationsTest.MainWindow.ServiceContinuation
WpfContinuationsTest.MainWindow.<Button_Click>b__0
System.Threading.Tasks.Task`1+<>c__DisplayClass17.<ContinueWith>b__16
System.Threading.Tasks.Task.InnerInvoke
System.Threading.Tasks.Task.Execute
System.Threading.Tasks.Task.ExecutionContextCallback
System.Threading.ExecutionContext.runTryCode
System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup
System.Threading.ExecutionContext.RunInternal
System.Threading.ExecutionContext.Run
System.Threading.Tasks.Task.ExecuteWithThreadLocal
System.Threading.Tasks.Task.ExecuteEntry
System.Threading.Tasks.SynchronizationContextTaskScheduler.PostCallback

成功 - 不调用 WCF 服务

WpfContinuationsTest.MainWindow.ServiceContinuation
WpfContinuationsTest.MainWindow.<Button_Click>b__0
System.Threading.Tasks.Task`1+<>c__DisplayClass17.<ContinueWith>b__16
System.Threading.Tasks.Task.InnerInvoke
System.Threading.Tasks.Task.Execute
System.Threading.Tasks.Task.ExecutionContextCallback
System.Threading.ExecutionContext.Run
System.Threading.Tasks.Task.ExecuteWithThreadLocal
System.Threading.Tasks.Task.ExecuteEntry
System.Threading.Tasks.SynchronizationContextTaskScheduler.PostCallback

有谁知道为什么,当唯一的区别是 WCF 客户端服务调用(没有回调合同)时,在一种情况下,主线程上的延续会具有 SynchronizationContext,而在另一种情况下则不会?

【问题讨论】:

  • 在发布问题之前,请搜索类似问题。这可能与Why is SynchronizationContext.Current null in my Winforms application? 问题重复。
  • @Rest Wing - 这不是那个问题的重复;我将 TaskScheduler.FromCurrentSynchronizationContext 存储在 MainWindow 的构造函数中,并且我还声明 SchedulerContinuation 方法确实在主 UI 线程上执行(使用调试器中的托管线程 ID 进行验证),但是在继续期间,SynchronizationContext.Current 为 null .但是,如果我在后台线程中注释掉 WCF 方法,我在延续中确实有一个 SynchronizationContext。

标签: wpf wcf task synchronizationcontext


【解决方案1】:

根据 Microsoft 的说法,这是 TPL 的一个已知错误:

http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/629d5524-c8db-466f-bc27-0ced11b441ba

【讨论】:

  • 哇,我刚刚在同一个问题上花了半天时间,直到你的帖子在互联网上一无所获。谢谢!希望他们尽快修复这个错误。
  • 好消息是,该问题已在 .Net 4.5 中得到修复。不幸的是,由于这是一个就地升级,这意味着如果您从 4.5 的开发盒中以 .Net 4.0 为目标,您将错过用户可能遇到的问题。所以还是要小心!
猜你喜欢
  • 1970-01-01
  • 2019-10-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多