C# SynchronizationContext.Current在主UI线程的延续中为null
我一直试图在Winforms应用程序中跟踪以下问题:C# SynchronizationContext.Current在主UI线程的延续中为null,c#,winforms,task-parallel-library,synchronizationcontext,C#,Winforms,Task Parallel Library,Synchronizationcontext,我一直试图在Winforms应用程序中跟踪以下问题: 在主线程上运行的任务延续(即.ContinueWith)中,同步上下文.Current为空(我希望当前同步上下文为System.Windows.Forms.WindowsFormsSynchronizationContext) 下面是Winforms代码,演示了该问题: using System; using System.Threading; using System.Threading.Tasks; using System.Window
在主线程上运行的任务延续(即
.ContinueWith
)中,同步上下文.Current
为空(我希望当前同步上下文为System.Windows.Forms.WindowsFormsSynchronizationContext
)
下面是Winforms代码,演示了该问题:
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
TaskScheduler ts = TaskScheduler.FromCurrentSynchronizationContext(); // Get the UI task scheduler
// This line is required to see the issue (Removing this causes the problem to go away), since it changes the codeflow in
// \SymbolCache\src\source\.NET\4\DEVDIV_TFS\Dev10\Releases\RTMRel\ndp\clr\src\BCL\System\Threading\ExecutionContext.cs\1305376\ExecutionContext.cs
// at line 435
System.Diagnostics.Trace.CorrelationManager.StartLogicalOperation("LogicalOperation");
var task = Task.Factory.StartNew(() => { });
var cont = task.ContinueWith(MyContinueWith, CancellationToken.None, TaskContinuationOptions.None, ts);
System.Diagnostics.Trace.CorrelationManager.StopLogicalOperation();
}
void MyContinueWith(Task t)
{
if (SynchronizationContext.Current == null) // The current SynchronizationContext shouldn't be null here, but it is.
MessageBox.Show("SynchronizationContext.Current is null");
}
}
}
这对我来说是一个问题,因为我试图从延续中使用BackgroundWorker
,而BackgroundWorker将使用当前同步上下文来处理其事件RunWorkerCompleted
和ProgressChanged
。因为当我启动BackgroundWorker时,当前SynchronizationContext为null,所以事件不会像我所希望的那样在主ui线程上运行
我的问题:这是微软代码中的一个错误,还是我在某个地方犯了错误 其他信息:
- 我正在使用.NET4.0(我还没有在.NET4.5RC上尝试过)
- 我可以在任何x86/x64/任何CPU(在x64机器上)上的调试/发行版上重现这一点
- 它不断地复制(如果有人不能复制,我会感兴趣)
- 我有使用BackgroundWorker的遗留代码,所以我不能轻易地改为不使用BackgroundWorker
- 我已经确认
中的代码正在主ui线程上运行MyContinueWith
- 我不知道为什么调用
会导致问题,这正是我在应用程序中缩小范围的原因操作
从调试到.Net源代码中,我对问题何时重现有了更好的理解。下面是ExecutionContext.cs中的一些相关代码:
internal static void Run(ExecutionContext executionContext, ContextCallback callback, Object state, bool ignoreSyncCtx)
{
// ... Some code excluded here ...
ExecutionContext ec = Thread.CurrentThread.GetExecutionContextNoCreate();
if ( (ec == null || ec.IsDefaultFTContext(ignoreSyncCtx)) &&
#if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK
SecurityContext.CurrentlyInDefaultFTSecurityContext(ec) &&
#endif // #if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK
executionContext.IsDefaultFTContext(ignoreSyncCtx))
{
callback(state);
}
else
{
if (executionContext == s_dummyDefaultEC)
executionContext = s_dummyDefaultEC.CreateCopy();
RunInternal(executionContext, callback, state);
}
}
只有当我们进入调用RunInternal的“else”子句时,问题才会重现。这是因为RunInternal最终会替换ExecutionContext,从而更改当前SynchronizationContext的内容:
// Get the current SynchronizationContext on the current thread
public static SynchronizationContext Current
{
get
{
SynchronizationContext context = null;
ExecutionContext ec = Thread.CurrentThread.GetExecutionContextNoCreate();
if (ec != null)
{
context = ec.SynchronizationContext;
}
// ... Some code excluded ...
return context;
}
}
因此,对于我的具体情况,这是因为行`executionContext.IsDefaultFTContext(ignoreSyncCtx))返回false。这是代码:
internal bool IsDefaultFTContext(bool ignoreSyncCtx)
{
#if FEATURE_CAS_POLICY
if (_hostExecutionContext != null)
return false;
#endif // FEATURE_CAS_POLICY
#if FEATURE_SYNCHRONIZATIONCONTEXT
if (!ignoreSyncCtx && _syncContext != null)
return false;
#endif // #if FEATURE_SYNCHRONIZATIONCONTEXT
#if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK
if (_securityContext != null && !_securityContext.IsDefaultFTSecurityContext())
return false;
#endif //#if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK
if (_logicalCallContext != null && _logicalCallContext.HasInfo)
return false;
if (_illogicalCallContext != null && _illogicalCallContext.HasUserData)
return false;
return true;
}
public bool HasInfo
{
[System.Security.SecurityCritical] // auto-generated
get
{
bool fInfo = false;
// Set the flag to true if there is either remoting data, or
// security data or user data
if(
(m_RemotingData != null && m_RemotingData.HasInfo) ||
(m_SecurityData != null && m_SecurityData.HasInfo) ||
(m_HostContext != null) ||
HasUserData
)
{
fInfo = true;
}
return fInfo;
}
}
internal bool HasUserData
{
get { return ((m_Datastore != null) && (m_Datastore.Count > 0));}
}
对我来说,由于\u logicalCallContext.HasInfo
为真,所以返回false。这是代码:
internal bool IsDefaultFTContext(bool ignoreSyncCtx)
{
#if FEATURE_CAS_POLICY
if (_hostExecutionContext != null)
return false;
#endif // FEATURE_CAS_POLICY
#if FEATURE_SYNCHRONIZATIONCONTEXT
if (!ignoreSyncCtx && _syncContext != null)
return false;
#endif // #if FEATURE_SYNCHRONIZATIONCONTEXT
#if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK
if (_securityContext != null && !_securityContext.IsDefaultFTSecurityContext())
return false;
#endif //#if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK
if (_logicalCallContext != null && _logicalCallContext.HasInfo)
return false;
if (_illogicalCallContext != null && _illogicalCallContext.HasUserData)
return false;
return true;
}
public bool HasInfo
{
[System.Security.SecurityCritical] // auto-generated
get
{
bool fInfo = false;
// Set the flag to true if there is either remoting data, or
// security data or user data
if(
(m_RemotingData != null && m_RemotingData.HasInfo) ||
(m_SecurityData != null && m_SecurityData.HasInfo) ||
(m_HostContext != null) ||
HasUserData
)
{
fInfo = true;
}
return fInfo;
}
}
internal bool HasUserData
{
get { return ((m_Datastore != null) && (m_Datastore.Count > 0));}
}
对我来说,这返回true是因为HasUserData是true。这是代码:
internal bool IsDefaultFTContext(bool ignoreSyncCtx)
{
#if FEATURE_CAS_POLICY
if (_hostExecutionContext != null)
return false;
#endif // FEATURE_CAS_POLICY
#if FEATURE_SYNCHRONIZATIONCONTEXT
if (!ignoreSyncCtx && _syncContext != null)
return false;
#endif // #if FEATURE_SYNCHRONIZATIONCONTEXT
#if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK
if (_securityContext != null && !_securityContext.IsDefaultFTSecurityContext())
return false;
#endif //#if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK
if (_logicalCallContext != null && _logicalCallContext.HasInfo)
return false;
if (_illogicalCallContext != null && _illogicalCallContext.HasUserData)
return false;
return true;
}
public bool HasInfo
{
[System.Security.SecurityCritical] // auto-generated
get
{
bool fInfo = false;
// Set the flag to true if there is either remoting data, or
// security data or user data
if(
(m_RemotingData != null && m_RemotingData.HasInfo) ||
(m_SecurityData != null && m_SecurityData.HasInfo) ||
(m_HostContext != null) ||
HasUserData
)
{
fInfo = true;
}
return fInfo;
}
}
internal bool HasUserData
{
get { return ((m_Datastore != null) && (m_Datastore.Count > 0));}
}
对我来说,由于我调用了Diagnostics.Trace.CorrelationManager.StartLogicalOperation(“LogicalOperation”),m_数据存储中会有一些项代码>
总之,看起来有几种不同的方法可以让bug重现。希望此示例能够帮助其他人确定他们是否遇到了相同的错误。您可以联系Microsoft PSS,查看是否可以获得修补程序。我在这里提交了一份错误报告:您的错误已关闭,无法复制:(这是在一位Microsoft代表说“我们能够在.NetFramework 4.0上重现此问题,并确认它已在我们的下一个版本中修复。”我想知道这是在4.0中修复的还是仅在4.5中修复的。我必须在4.0中解决它。(Matt,你找到4.0的解决方法了吗?我有一个可移植类(配置文件158)这就是我在4.0版中遇到的问题。啊,我在NUnit测试中这样做,可能有诊断跟踪调用…好吧,你从那个链接得到了诊断。你是否也在这个Winforms应用程序中混合了WPF或WCF代码?