WPF + Tasks + WCF = No SynchronizationContext?
Asked Answered
M

1

7

I have a WPF application that is using System.Threading.Tasks to call a WCF service in the background. I'm using Task.ContinueWith to return the results of the service call to the WPF UI thread. My issue is that, although the continuation does run on the UI thread, when it does SynchronizationContext.Current is null. I can run the same code, commenting out the WCF call in the initial Task, and the continuation is on the UI thread, with a DispatcherSynchronizationContext as expected.

The WCF proxy is generated using ChannelFactory, and uses wsHttpBinding. There is no callback contract. The relevant code is shown below:

    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 }

If I run this code as is, the ServiceContinuation is called on the correct thread (verified using ManagedThreadID), but SynchronizationContext.Current is null. If I comment out the single line that makes the service call (result = service.TheServiceMethod();), then ServiceContinuation is correctly called with a DispatcherSynchronizationContext.

One note - the SynchronizationContext is not permanently lost - if I Click on the button again, the button click handler does have the correct SynchronizationContext.

I've captured stack traces for the two cases; they have a few differences. I've left out all of the bits that are identical, and only included the top of the stacks where they differ, plus a few frames for reference:

Fails - Calls WCF Service

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

Succeeds - No Call To WCF Service

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

Does anyone know why, when the only difference is a WCF client service call (with no callback contract), in one case the continuation on the main thread would have a SynchronizationContext, and in the other case it wouldn't?

Mercantile answered 18/2, 2011 at 16:21 Comment(2)
Before posting a question, search for a similar one. This is a possible duplicate of Why is SynchronizationContext.Current null in my Winforms application? question.Gentlemanly
@Rest Wing - This is not a duplicate of that question; I am storing TaskScheduler.FromCurrentSynchronizationContext in the constructor of MainWindow, and I also stated that the SchedulerContinuation method was indeed executing on the main UI thread (verified using Managed Thread ID in the debugger), but that during that continuation, SynchronizationContext.Current is null. However, if I comment out the WCF method in the background thread, I do have a SynchronizationContext in the continuation.Mercantile
M
7

According to Microsoft, this is a known bug with the TPL:

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

Mercantile answered 22/2, 2011 at 8:19 Comment(2)
Wow, I just spent half of the day on the same problem and found nothing on the Internet until your post. Thank you! Hope they fix this bug soon.Steerageway
The good news is, the problem is fixed in .Net 4.5. Unfortunately, as this is an in-place upgrade, it means that if you target .Net 4.0 from a dev box with 4.5, you will miss problems that your users could run into. So still beware!Dannielledannon

© 2022 - 2024 — McMap. All rights reserved.