2012-07-23 36 views
20

Tôi đã cố gắng theo dõi vấn đề sau trong ứng dụng Winforms:
Số SynchronizationContext.Current không được tiếp tục công việc (tức là .ContinueWith) đang chạy trên chủ đề chính (Tôi mong đợi bối cảnh đồng bộ hóa hiện tại là System.Windows.Forms.WindowsFormsSynchronizationContext).SynchronizationContext.Current is null trong Tiếp tục trên chủ đề giao diện người dùng chính

Dưới đây là đoạn code Winforms chứng minh vấn đề:

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

Đây là một vấn đề đối với tôi kể từ khi tôi cố gắng sử dụng BackgroundWorker từ việc tiếp tục, và BackgroundWorker sẽ sử dụng SynchronizationContext hiện cho các sự kiện của nó RunWorkerCompletedProgressChanged. Kể từ khi SynchronizationContext hiện tại là null khi tôi khởi động BackgroundWorker, các sự kiện không chạy trên thread ui chính như tôi dự định.

Câu hỏi của tôi:
Đây có phải là lỗi trong mã của Microsoft hay tôi đã phạm sai lầm ở đâu đó?

Thông tin thêm:

  • Tôi đang sử dụng Net 4.0 (tôi chưa thử này trên NET 4.5 RC)
  • tôi có thể tái sản xuất này trên cả hai Debug/phát hành trên bất kỳ x86/x64/Bất kỳ CPU nào (trên máy x64).
  • Nó tái tạo một cách nhất quán (tôi muốn được quan tâm nếu có ai không thể tái tạo nó).
  • Tôi có mã cũ sử dụng BackgroundWorker - vì vậy tôi không thể dễ dàng thay đổi để không sử dụng BackgroundWorker
  • Tôi đã xác nhận rằng mã trong MyContinueWith đang chạy trên chuỗi ui chính.
  • Tôi không biết chính xác lý do tại sao cuộc gọi StartLogicalOperation giúp gây ra sự cố, đó chỉ là những gì tôi đã thu hẹp nó xuống trong đơn đăng ký của mình.
+0

Vâng, bạn có chẩn đoán từ liên kết đó. Bạn cũng đang trộn mã WPF hoặc WCF vào ứng dụng Winforms này? –

+0

Không - Chỉ có dạng Winforms. –

Trả lời

20

Sự cố được khắc phục trong .NET 4.5 RC (chỉ cần kiểm tra). Vì vậy, tôi cho rằng đó là một lỗi trong .NET 4.0. Ngoài ra, tôi đoán rằng những bài viết được tham khảo cùng một vấn đề:

Đó là bất hạnh. Bây giờ tôi phải xem xét cách giải quyết.

Edit:
Từ gỡ lỗi vào nguồn Net, tôi có một chút hiểu rõ hơn về vấn đề này khi sẽ sinh sản. Dưới đây là một số mã có liên quan từ 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); 
      } 
     } 

Sự cố chỉ tái tạo khi chúng tôi chuyển sang mệnh đề "khác" gọi là RunInternal.Điều này là do RunInternal kết thúc lên thay thế các ExecutionContext trong đó có các tác động của thay đổi những gì SynchronizationContext hiện tại:

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

Vì vậy, đối với trường hợp cụ thể của tôi, đó là vì dòng 'executionContext.IsDefaultFTContext (ignoreSyncCtx)) trở lại sai. Đây là mã số:

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

Đối với tôi, đã trả về false do _logicalCallContext.HasInfo là đúng. Đây là mã:

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

Đối với tôi, điều này đã trở lại đúng vì HasUserData là đúng. Dưới đây là mã:

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

Đối với tôi, m_DataStore sẽ có các mục trong nó do kêu gọi của Mẹ để Diagnostics.Trace.CorrelationManager.StartLogicalOperation("LogicalOperation");

Nói tóm lại, có vẻ như có rất nhiều cách khác nhau bạn có thể nhận lỗi để sinh sản. Hy vọng rằng, ví dụ này sẽ phục vụ để giúp đỡ người khác trong việc xác định xem họ đang chạy vào cùng một lỗi này hay không.

+1

Bạn có thể liên hệ với Microsoft PSS để xem bạn có thể nhận hotfix cho nó không. –

+3

Tôi đã gửi báo cáo lỗi tại đây: https://connect.microsoft.com/VisualStudio/feedback/details/755320/synchronizationcontext-current-is-null-in-continuation-on-the-main-ui-thread –

+0

Lỗi của bạn đã bị đóng không thể sao chép được: (và đó là sau khi một trong các đại diện của Microsoft nói "Chúng tôi đã có thể tạo lại vấn đề này trên .NetFramework 4.0 và xác nhận rằng nó đã được khắc phục trong bản phát hành tiếp theo của chúng tôi." Tôi muốn biết điều này có được khắc phục không trong 4.0 hoặc chỉ 4.5. – jpierson

Các vấn đề liên quan