WPF + Задачи + WCF = Нет контекста синхронизации?

У меня есть приложение 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.

Одно примечание: 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, а в другом случае - нет?


person Travis H    schedule 18.02.2011    source источник
comment
Прежде чем задавать вопрос, поищите похожий. Это возможный дубликат Почему SynchronizationContext.Current null в моем Приложение Winforms? вопрос.   -  person Rest Wing    schedule 18.02.2011
comment
@Rest Wing - это не дубликат этого вопроса; Я сохраняю TaskScheduler.FromCurrentSynchronizationContext в конструкторе MainWindow, и я также заявил, что метод SchedulerContinuation действительно выполнялся в основном потоке пользовательского интерфейса (проверено с помощью идентификатора управляемого потока в отладчике), но во время этого продолжения SynchronizationContext.Current имеет значение null . Однако, если я закомментирую метод WCF в фоновом потоке, у меня будет SynchronizationContext в продолжении.   -  person Travis H    schedule 19.02.2011


Ответы (1)


Согласно Microsoft, это известная ошибка в TPL:

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

person Travis H    schedule 22.02.2011
comment
Вау, я только что потратил полдня на ту же проблему и ничего не нашел в Интернете до вашего поста. Спасибо! Надеюсь, они скоро исправят этот баг. - person Julien Lebosquain; 02.03.2011
comment
Хорошая новость в том, что проблема исправлена ​​в .Net 4.5. К сожалению, поскольку это обновление на месте, это означает, что если вы нацелитесь на .Net 4.0 из коробки разработчика с 4.5, вы пропустите проблемы, с которыми могут столкнуться ваши пользователи. Так что все же берегитесь! - person Mark; 01.11.2013