c# - 主线程的 SynchronizationContext.Current 如何在 Windows 窗体应用程序中变为 null?

标签 c# wpf winforms task-parallel-library

我的应用程序出现问题:在某些时候,主线程的 SynchronizationContext.Current 变为 null。我无法在一个孤立的项目中重现同样的问题。我的真实项目很复杂;它混合了 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 变为 null?

编辑:

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


   at MyApp.Framework.UI.Commands.AsyncCommand.HandleTaskError(Task task) in d:\sources\s2\Framework\Sources\UI\Commands\AsyncCommand.cs:line 157
   at System.Threading.Tasks.Task.c__DisplayClassb.b__a(Object obj)
   at System.Threading.Tasks.Task.InnerInvoke()
   at System.Threading.Tasks.Task.Execute()
   at System.Threading.Tasks.Task.ExecutionContextCallback(Object obj)
   at System.Threading.ExecutionContext.runTryCode(Object userData)
   at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
   at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot)
   at System.Threading.Tasks.Task.ExecuteEntry(Boolean bPreventDoubleExecution)
   at System.Threading.Tasks.SynchronizationContextTaskScheduler.PostCallback(Object obj)
   at System.RuntimeMethodHandle._InvokeMethodFast(IRuntimeMethodInfo method, Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeType typeOwner)
   at System.RuntimeMethodHandle.InvokeMethodFast(IRuntimeMethodInfo method, Object target, Object[] arguments, Signature sig, MethodAttributes methodAttributes, RuntimeType typeOwner)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)
   at System.Delegate.DynamicInvokeImpl(Object[] args)
   at System.Windows.Forms.Control.InvokeMarshaledCallbackDo(ThreadMethodEntry tme)
   at System.Windows.Forms.Control.InvokeMarshaledCallbackHelper(Object obj)
   at System.Threading.ExecutionContext.runTryCode(Object userData)
   at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Windows.Forms.Control.InvokeMarshaledCallback(ThreadMethodEntry tme)
   at System.Windows.Forms.Control.InvokeMarshaledCallbacks()
   at System.Windows.Forms.Control.WndProc(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
   at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
   at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
   at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData)
   at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
   at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
   at System.Windows.Forms.Application.Run(Form mainForm)
   at MyApp.Framework.SharedUI.ApplicationBase.InternalStart() in d:\sources\s2\Framework\Sources\UI\SharedUI\ApplicationBase.cs:line 190
   at MyApp.Framework.SharedUI.ApplicationBase.Start() in d:\sources\s2\Framework\Sources\UI\SharedUI\ApplicationBase.cs:line 118
   at MyApp.App1.WinUI.HDA.Main() in d:\sources\s2\App1\Sources\WinUI\HDA.cs:line 63

最佳答案

Sly,当混合使用 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();

根据 this post在 msdn 论坛上,这是 4.0 中 TPL 中已确认的错误。同事在 4.5 上运行并且没有看到此行为。

我们通过使用 FromCurrentSynchronizationContext 在主线程的静态单例中创建一个 TaskScheduler 来解决这个问题,然后在创建延续时始终引用该任务调度程序。例如

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

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

更新 如果您的开发机器上安装了 .net 4.5,即使您的目标是 4.0 框架,您也不会看到这个问题。仅安装 4.0 的用户仍会受到影响。

关于c# - 主线程的 SynchronizationContext.Current 如何在 Windows 窗体应用程序中变为 null?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4659257/

相关文章:

c# - 添加双 httpHeaders

c# - UWP 中 AppDomain.GetAssemblies() 的等价性?

c# - ReportViewer RDLC 图表差距

c# - winforms 中的 TreeView 错误

C#获取焦点所在的Windows资源管理器路径

c# - WPF 根据内容将标签背景设置为图像

wpf - WPF 中的主题化用户控件

.net - 使用功能区作为选项卡控件

wpf - 如何使用 WPF 单击一次应用程序部署非托管 DLL?

c# - KeyDown 未拾取 'Return' 键 C#