Как SynchronizationContext.Current основного streamа становится нулевым в приложении Windows Forms?

У меня проблема в моем приложении: в какой-то момент SynchronizationContext.Current становится нулевым для основного streamа. Я не могу воспроизвести ту же проблему в изолированном проекте. Мой настоящий проект сложный; он смешивает Windows Forms и WPF и вызывает веб-службы WCF. Насколько я знаю, это все системы, которые могут взаимодействовать с SynchronizationContext.

Это код из моего изолированного проекта. Мое настоящее приложение делает что-то похожее на это. Однако в моем реальном приложении SynchronizationContext.Current имеет значение null в основном streamе, когда выполняется задача продолжения.

private void button2_Click(object sender, EventArgs e) { if (SynchronizationContext.Current == null) { Debug.Fail("SynchronizationContext.Current is null"); } Task.Factory.StartNew(() => { CallWCFWebServiceThatThrowsAnException(); }) .ContinueWith((t) => { //update the UI UpdateGUI(t.Exception); if (SynchronizationContext.Current == null) { Debug.Fail("SynchronizationContext.Current is null"); } }, CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.FromCurrentSynchronizationContext()); } 

Что может привести к тому, что SynchronizationContext.Current основного streamа станет нулевым?

Редактировать:

@Hans попросил трассировку стека. Вот:

    в MyApp.Framework.UI.Commands.AsyncCommand.HandleTaskError (задача задачи) в d: \ sources \ s2 \ Framework \ Sources \ UI \ Commands \ AsyncCommand.cs: строка 157
    в System.Threading.Tasks.Task.c__DisplayClassb.b__a (Object obj)
    в System.Threading.Tasks.Task.InnerInvoke ()
    в System.Threading.Tasks.Task.Execute ()
    в System.Threading.Tasks.Task.ExecutionContextCallback (Object obj)
    в System.Threading.ExecutionContext.runTryCode (Object userData)
    в System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup (код TryCode, CleanoutCode CleanoutCode, Object userData)
    в System.Threading.ExecutionContext.RunInternal (ExecutionContext executeContext, обратный вызов ContextCallback, состояние объекта)
    в System.Threading.ExecutionContext.Run (ExecutionContext executeContext, обратный вызов ContextCallback, состояние объекта, Boolean ignoreSyncCtx)
    в System.Threading.Tasks.Task.ExecuteWithThreadLocal (Task & currentTaskSlot)
    в System.Threading.Tasks.Task.ExecuteEntry (Boolean bPreventDoubleExecution)
    в System.Threading.Tasks.SynchronizationContextTaskScheduler.PostCallback (Object obj)
    в System.RuntimeMethodHandle._InvokeMethodFast (метод IRuntimeMethodInfo, Object target, Object [] arguments, SignatureStruct & sig, MethodAttributes methodAttributes, RuntimeType typeOwner)
    в System.RuntimeMethodHandle.InvokeMethodFast (метод IRuntimeMethodInfo, Object target, Object [] arguments, Signature sig, MethodAttributes methodAttributes, RuntimeType typeOwner)
    в System.Reflection.RuntimeMethodInfo.Invoke (Object obj, BindingFlags invokeAttr, Binder binder, Object [] parameters, CultureInfo culture, Boolean skipVisibilityChecks)
    в System.Delegate.DynamicInvokeImpl (Object [] args)
    в System.Windows.Forms.Control.InvokeMarshaledCallbackDo (ThreadMethodEntry tme)
    в System.Windows.Forms.Control.InvokeMarshaledCallbackHelper (Object obj)
    в System.Threading.ExecutionContext.runTryCode (Object userData)
    в System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup (код TryCode, CleanoutCode CleanoutCode, Object userData)
    в System.Threading.ExecutionContext.RunInternal (ExecutionContext executeContext, обратный вызов ContextCallback, состояние объекта)
    в System.Threading.ExecutionContext.Run (ExecutionContext executeContext, обратный вызов ContextCallback, состояние объекта, Boolean ignoreSyncCtx)
    в System.Threading.ExecutionContext.Run (ExecutionContext executeContext, обратный вызов ContextCallback, состояние объекта)
    в System.Windows.Forms.Control.InvokeMarshaledCallback (ThreadMethodEntry tme)
    в System.Windows.Forms.Control.InvokeMarshaledCallbacks ()
    в System.Windows.Forms.Control.WndProc (Message & m)
    в System.Windows.Forms.Control.ControlNativeWindow.OnMessage (Message & m)
    в System.Windows.Forms.Control.ControlNativeWindow.WndProc (Message & m)
    в System.Windows.Forms.NativeWindow.Callback (IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
    в System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW (MSG & msg)
    в System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop (IntPtr dwComponentID, причина Int32, Int32 pvLoopData)
    в System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner (причина Int32, контекст ApplicationContext)
    в System.Windows.Forms.Application.ThreadContext.RunMessageLoop (причина Int32, контекст ApplicationContext)
    в System.Windows.Forms.Application.Run (Form mainForm)
    в MyApp.Framework.SharedUI.ApplicationBase.InternalStart () в d: \ sources \ s2 \ Framework \ Sources \ UI \ SharedUI \ ApplicationBase.cs: строка 190
    в MyApp.Framework.SharedUI.ApplicationBase.Start () в d: \ sources \ s2 \ Framework \ Sources \ UI \ SharedUI \ ApplicationBase.cs: строка 118
    в MyApp.App1.WinUI.HDA.Main () в d: \ sources \ s2 \ App1 \ Sources \ WinUI \ HDA.cs: строка 63

Хитрый, я столкнулся с тем же самым поведением, когда используется смесь WPF, WCF и TPL. Текущий SynchronizationContext основного streamа станет нулевым в нескольких ситуациях.

 var context = SynchronizationContext.Current; // if context is null, an exception of // The current SynchronizationContext may not be used as a TaskScheduler. // will be thrown TaskScheduler.FromCurrentSynchronizationContext(); 

Согласно этому сообщению на форумах msdn, это подтвержденная ошибка в TPL в 4.0. Сотрудник работает на 4.5 и не видит такого поведения.

Мы решили это, создав TaskScheduler в статическом singleton с основным streamом, используя FromCurrentSynchronizationContext, а затем всегда ссылайтесь на этот планировщик задач при создании продолжений. Например

 Task task = Task.Factory.StartNew(() => { // something } ).ContinueWith(t => { // ui stuff }, TheSingleton.Current.UiTaskScheduler); 

Это позволяет избежать проблемы в TPL на .net 4.0.

Обновление Если на вашей машине разработки установлен .net 4.5, эта проблема не будет отображаться, даже если вы настроите таргетинг на фреймворк 4.0. Ваши пользователи, у которых установлен только 4.0, по-прежнему будут затронуты.

Не уверен, что это предпочтительный метод, но вот как я использую SynchronizationContext:

В вашем конструкторе (основной stream) сохраните копию текущего контекста, таким образом вы гарантируете (??), чтобы иметь правильный контекст позже, независимо от того, какой stream вы используете.

 _uiCtx = SynchronizationContext.Current; 

И позже в вашей Задаче используйте это, чтобы взаимодействовать с основным streamом пользовательского интерфейса

 _uiCtx.Post( ( o ) => { //UI Stuff goes here }, null ); 

Я создал для этого class. Это выглядит так:

 public class UIContext { private static TaskScheduler m_Current; public static TaskScheduler Current { get { return m_Current; } private set { m_Current = value; } } public static void Initialize() { if (Current != null) return; if (SynchronizationContext.Current == null) SynchronizationContext.SetSynchronizationContext(new SynchronizationContext()); Current = TaskScheduler.FromCurrentSynchronizationContext(); } } 

При запуске моего приложения я вызываю UIContext.Initialize ()

И когда мне это нужно в задаче, я просто ставил UIContext.Current как TaskScheduler.

 Task.Factory.StartNew(() => { //Your code here }, CancellationToken.None, TaskCreationOptions.None, UIContext.Current); 
  • page.DataContext не унаследован от родительского фрейма?
  • Перетаскивание из источника данных в окно WPF не работает
  • Используйте «real» CultureInfo.CurrentCulture в привязке WPF, а не CultureInfo от IetfLanguageTag
  • В чем разница между свойствами Dependency SetValue () и SetCurrentValue ()
  • Контекстное меню WPF в левом клике
  • Связывание со статическим свойством
  • Каков наилучший способ переключения представлений / пользовательских элементов в MVVM-light и WPF?
  • Как остановить Wpf Tabcontrol, чтобы выгрузить визуальное дерево при изменении табуляции
  • Почему RelayCommand
  • Можно ли использовать другой шаблон для выбранного элемента в WPF ComboBox, чем для элементов в раскрывающемся списке?
  • Преобразование массива байтов в изображение в wpf
  • Давайте будем гением компьютера.