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