WPF + Tasks + WCF = No SynchronizationContext?

У меня есть приложение WPF, которое использует System.Threading.Tasks для вызова службы WCF в фоновом режиме. Я использую Task. ContinueWith, чтобы вернуть результаты вызова службы в поток пользовательского интерфейса WPF. Моя проблема в том, что, хотя продолжение действительно выполняется в потоке пользовательского интерфейса, когда он выполняет SynchronizationContext.Current имеет значение null. Я могу запустить тот же код, закомментировав вызов WCF в начальной задаче, и продолжение будет в потоке пользовательского интерфейса с 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 имеет значение null. Если я закомментирую единственную строку, которая вызывает вызов службы (result = service.TheServiceMethod ();), тогда ServiceContinuation правильно вызывается с помощью DispatcherSynchronizationContext.

Одно примечание - контекст синхронизации не теряется навсегда - если я еще раз нажму кнопку, обработчик нажатия кнопки будет иметь правильный контекст синхронизации.

Я записал трассировки стека для два случая; у них есть несколько отличий. Я исключил все идентичные биты и включил только верхние части стеков, где они различаются, плюс несколько кадров для справки:

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

Кто-нибудь знает, почему, когда единственная разница - это вызов клиентской службы WCF (без контракта обратного вызова), в одном случае продолжение в основном потоке будет иметь контекст синхронизации, а в другом - нет?

7
задан Travis H 19 February 2011 в 00:35
поделиться