主窗口的SynchronizationContext.Current如何在Windows窗体应用程序中变为空?

我的应用程序中有一个问题:在某些时候,主线程的SynchronizationContext.Current变为空。 我无法在孤立的项目中重现相同的问题。 我真正的项目是复杂的, 它混合了Windows窗体和WPF,并调用WCF Web服务。 据我所知,这些都是可以与SynchronizationContext进行交互的系统。

这是我孤立项目的代码。 我真正的应用程序做类似的东西。 但是,在我的真实应用程序中,在继续执行任务时,主线程上的SynchronizationContext.Current为null。

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变为空?

编辑:

@Hans要求堆栈跟踪。 这里是:


   在d:\ sources \ s2 \ Framework \ Sources \ UI \ Commands \ AsyncCommand.cs中的MyApp.Framework.UI.Commands.AsyncCommand.HandleTaskError(任务任务):第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代码,CleanupCode backoutCode,对象userData)
   在System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext,ContextCallbackcallback,对象状态)
   在System.Threading.ExecutionContext.Run(ExecutionContext executionContext,ContextCallbackcallback,对象状态,布尔ignoreSyncCtx)
   在System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task&currentTaskSlot)
   在System.Threading.Tasks.Task.ExecuteEntry(布尔bPreventDoubleExecution)
   在System.Threading.Tasks.SynchronizationContextTaskScheduler.PostCallback(Object obj)
    (System.RuntimeMethodHandle._InvokeMethodFast(IRuntimeMethodInfo方法,Object target,Object []参数,SignatureStruct&sig,MethodAttributes methodAttributes,RuntimeType typeOwner)
   在System.RuntimeMethodHandle.InvokeMethodFast(IRuntimeMethodInfo方法,对象目标,对象[]参数,签名sig,MethodAttributes methodAttributes,RuntimeType typeOwner)
    (System.Reflection.RuntimeMethodInfo.Invoke(Object obj,BindingFlags invokeAttr,Binder binder,Object []参数,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代码,CleanupCode backoutCode,对象userData)
   在System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext,ContextCallbackcallback,对象状态)
   在System.Threading.ExecutionContext.Run(ExecutionContext executionContext,ContextCallbackcallback,对象状态,布尔ignoreSyncCtx)
   在System.Threading.ExecutionContext.Run(ExecutionContext executionContext,ContextCallbackcallback,对象状态)
   在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(消息&m)
   在System.Windows.Forms.Control.ControlNativeWindow.WndProc(消息&m)
   在System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd,Int32 msg,IntPtr wparam,IntPtr lparam)
   在System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(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(表单mainForm)
   在d:\ sources \ s2 \ Framework \ Sources \ UI \ SharedUI \ ApplicationBase.cs中的MyApp.Framework.SharedUI.ApplicationBase.InternalStart():line 190
   在d:\ sources \ s2 \ Framework \ Sources \ UI \ SharedUI \ ApplicationBase.cs中的MyApp.Framework.SharedUI.ApplicationBase.Start():第118行
   在d:\ sources \ s2 \ App1 \ Sources \ WinUI \ HDA.cs中的MyApp.App1.WinUI.HDA.Main()中:第63行

狡猾的,当我使用WPF,WCF和TPL的混合物时,我遇到了完全相同的行为。 主线程的当前SynchronizationContext在几种情况下将变为null。

 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论坛上的这个post ,这是4.0中TPL中的一个确认错误。 一个同事正在4.5上运行,并没有看到这种行为。

我们通过在主线程中使用FromCurrentSynchronizationContext在静态单例中创buildTaskScheduler来解决此问题,然后在创build延续时始终引用该任务计划程序。 例如

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

这避免了在.net 4.0上的TPL中的问题。

更新如果您的开发计算机上安装了.net 4.5,即使您的目标是4.0框架,也不会看到此问题。 只有4.0安装的用户将仍然受到影响。

不知道这是否是首选的方法,但这里是我如何使用SynchronizationContext:

在你的构造函数(主线程)中保存一个当前上下文的副本,这样就保证了(??)以后有正确的上下文,不pipe你在哪个线程上。

 _uiCtx = SynchronizationContext.Current; 

然后在你的任务中使用它来做与主UI线程交互

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

我为此创build了一个类。 它看起来像这样:

 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);