本文介绍了SynchronizationContext.Current为null延续在主UI线程上的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在试图追查在WinForms应用程序了以下问题:结果
SynchronizationContext.Current 是任务的延续空(即 .ContinueWith ),这是主要的线程上运行(我预计当前的同步上下文是 System.Windows.Forms.WindowsFormsSynchronizationContext )。

I've been trying to track down the following issue in a Winforms application:
The SynchronizationContext.Current is null in a task's continuation (i.e. .ContinueWith) which is run on the main thread (I expect the the current synchronization context to be System.Windows.Forms.WindowsFormsSynchronizationContext).

这里是的WinForms code演示问题:

Here's the Winforms code demonstrating the issue:

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线程上,因为我打算跑。

This is an issue for me since I attempt to use BackgroundWorker from the continuation, and the BackgroundWorker will use the current SynchronizationContext for its events RunWorkerCompleted and ProgressChanged. Since the current SynchronizationContext is null when I kick off the BackgroundWorker, the events don't run on the main ui thread as I intend.

我的问题:结果
这是微软的code错误,或者有什么地方我犯了一个错误?

My question:
Is this a bug in Microsoft's code, or have I made a mistake somewhere?

其他信息:


  • 我使用.NET 4.0(我还没有尝试过这种基于.NET 4.5 RC)

  • 我可以同时在调试/发布任何在x86 / x64 /任何CPU的复制本(在x64机器上)。

  • 它再现了一致(我有兴趣,如果任何人不能复制)。

  • 我有一个使用BackgroundWorker的传统code - 所以我不能轻易改过来不使用的BackgroundWorker

  • 我已经证实,在 MyContinueWith 的code是主UI线程上运行。

  • 我不知道precisely为什么 StartLogicalOperation 通话有助于引起的问题,这正是我将范围缩小到我的应用程序。

  • I'm using .Net 4.0 (I haven't yet tried this on .NET 4.5 RC)
  • I can reproduce this on both Debug/Release on any of x86/x64/Any CPU (on an x64 machine).
  • It reproduces consistently (I'd be interested if anyone can't reproduce it).
  • I have legacy code that uses the BackgroundWorker--so I can't easily change over to not using BackgroundWorker
  • I've confirmed that the code in MyContinueWith is running on the main ui thread.
  • I don't know precisely why the StartLogicalOperation call helps cause the issue, that's just what I narrowed it down to in my application.

推荐答案

该问题已在.NET 4.5 RC(只是测试它)。所以我假设它是在.NET 4.0中的一个错误。
另外,我猜测,这些职位都引用了同样的问题:

The issue is fixed in .NET 4.5 RC (just tested it). So I assume it is a bug in .NET 4.0.Also, I'm guessing that these posts are referencing the same issue:




  • How can SynchronizationContext.Current of the main thread become null in a Windows Forms application?
  • http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/629d5524-c8db-466f-bc27-0ced11b441ba

这是不幸的。现在我要考虑的解决方法。

That's unfortunate. Now I have to consider workarounds.

编辑:结果
从调试到.NET源代码,我有一点点更好地理解这一问题时会重现。下面是从ExecutionContext.cs一些相关的code:


From debugging into the .Net source, I have a little better understanding of when the issue would reproduce. Here's some relevant code from 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结束更换其具有改变的效果ExecutionContext中什么当前的SynchronizationContext:

The issue only reproduces when we get into the "else" clause which calls RunInternal. This is because the RunInternal ends up replacing the the ExecutionContext which has the effect of changing what the current 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。下面是code:

So, for my specific case, it was because the line `executionContext.IsDefaultFTContext(ignoreSyncCtx)) returned false. Here's that code:

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

对于我来说,这是由于 _logicalCallContext.HasInfo 返回false,是真实的。下面是code:

For me, that was returning false due to _logicalCallContext.HasInfo was true. Here's that code:

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是真实的。下面是code:

For me, this was returning true because HasUserData was true. Here's that code:

    internal bool HasUserData
    {
        get { return ((m_Datastore != null) && (m_Datastore.Count > 0));} 
    }

对于我来说,m_DataStore将有它的项目,由于我呼吁 Diagnostics.Trace.CorrelationManager.StartLogicalOperation(LogicalOperation);

总之,它看起来像有几种不同的方法,你可以得到的bug重现。希望这个例子可以帮助他人确定,如果他们遇到了同样的错误或没有。

In summary, it looks like there are several different ways you could get the bug to reproduce. Hopefully, this example will serve to help others in determining if they are running into this same bug or not.

这篇关于SynchronizationContext.Current为null延续在主UI线程上的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-21 08:34