Как 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а станет нулевым?
- Динамическая компоновка WPF с элементами управления и сеткой
- WPF: как программно удалить фокус из TextBox
- WPF привязка данных к интерфейсу, а не к фактическому объекту - возможность литья?
- Что означает «{Binding Path =.}» В привязке WPF?
- Нумерованный список
Редактировать:
@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 Datagrid?
- Как я могу преобразовать 'System.Windows.Input.Key' в 'System.Windows.Forms.Keys'?
- Измените элементы управления WPF из не основного streamа с помощью Dispatcher.Invoke
- WPF Get Element (s) под мышью
- Как получить размер текущего экрана в WPF?
- Как я могу программно генерировать события нажатия клавиш на C #?
- Как использовать привязки WPF с RelativeSource?
Хитрый, я столкнулся с тем же самым поведением, когда используется смесь 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);