7

我有一个 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,而在另一种情况下则不会?

4

1 回答 1

7

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

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

于 2011-02-22T08:19:05.673 回答