我一直在试图追查一个WinForms应用了以下问题:
该SynchronizationContext.Current
是在任务的延续(即空.ContinueWith
这是主要的线程上运行)(我预计当前同步上下文是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的将使用当前的SynchronizationContext其事件RunWorkerCompleted
和ProgressChanged
。 由于目前的SynchronizationContext为null,当我开球BackgroundWorker的,该事件不会在主UI线程上,因为我打算跑。
我的问题:
这是微软的代码中的错误,或者我有什么地方弄错了?
附加信息:
- 我使用.NET 4.0(我还没有尝试过这个在.NET 4.5 RC)
- 我可以在任何的x86 / 64 /不限CPU(在x64机器上)的两个调试/发布重现此。
- 它始终如一地再现了(我有兴趣,如果任何人不能复制)。
- 我有一个使用BackgroundWorker的遗留代码 - 所以我不能轻易改过来不使用的BackgroundWorker
- 我已经证实,在代码
MyContinueWith
是在主UI线程上运行。 - 我不确知为什么
StartLogicalOperation
呼叫有助于引起的问题,这正是我在我的应用范围缩小到。
该问题被固定在.NET 4.5 RC(只是测试它)。 所以,我认为它是在.NET 4.0中的错误。 另外,我猜测,这些职位都引用了同样的问题:
- 主线程的SynchronizationContext.Current如何在成为空Windows窗体应用程序?
- http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/629d5524-c8db-466f-bc27-0ced11b441ba
那真不幸。 现在我要考虑的解决方法。
编辑:
从调试到.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了“其他”条款的问题仅再现。 这是因为RunInternal最终取代它有什么改变目前的SynchronizationContext的效果的ExecutionContext:
// 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;
}
对我来说,这是返回false由于_logicalCallContext.HasInfo
是真实的。 下面是代码:
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;
}
}
对我来说,这是返回true,因为HasUserData是真实的。 下面是代码:
internal bool HasUserData
{
get { return ((m_Datastore != null) && (m_Datastore.Count > 0));}
}
对我来说,m_DataStore会在它的项目,由于我呼吁Diagnostics.Trace.CorrelationManager.StartLogicalOperation("LogicalOperation");
总之,它看起来像有几种不同的方法,你可以得到的错误重现。 我们希望,这个例子可以帮助别人决定,如果他们正在运行到这个相同的错误或不。
文章来源: SynchronizationContext.Current is null in Continuation on the main UI thread