I have a WPF application that uses System.Threading.Tasks to call a WCF service in the background. I use Task.ContinueWith to return the results of a service call to the WPF UI thread. My problem is that although the continuation is done in the user interface thread, when this happens, SynchronizationContext.Current is null. I can run the same code by commenting out the WCF call in the initial task, and continuing in the UI thread, with a probability of DispatcherSynchronizationContext.
A WCF proxy is created using ChannelFactory and uses wsHttpBinding. There is no feedback agreement. The corresponding 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, ServiceContinuation is called in the correct thread (checked using ManagedThreadID), but SynchronizationContext.Current is null. If I comment on one line that calls a service call (result = service.TheServiceMethod ();), then ServiceContinuation is correctly called using DispatcherSynchronizationContext.
One note - SynchronizationContext is not lost forever - if I press the button again, the button click handler has the correct SynchronizationContext.
I grabbed a stack trace for two cases; they have several differences. I left all the bits that are identical and included only the top of the stacks where they differ, plus a few frames for reference:
Failure - 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
Achievements - no WCF service call
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 in calling the WCF client service (without a callback contract), in one case the continuation in the main thread will have a SynchronizationContext, and in the other case it will not?