主窗口的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);