2009-12-10 15 views
18

我需要一個定製SynchronizationContext說:尋找一個自定義的SynchronizationContext的例子(用於單元測試)

  • 旗下擁有運行一個單獨的線程「文章」和「發送」代表
  • 是否發送在它們的順序依次在
  • 發送需要

沒有其他的方法,我需要這個,所以我可以單元測試一些線程代碼,會說話的WinForm的實際應用。

在我寫自己的之前,我希望有人能指點我一個簡單的(和小的)實現。

回答

10

這一個是前一段時間我寫的,帶有版權沒有任何問題,也無法保證(系統沒有投入生產):

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading; 
using System.Windows.Threading; 

namespace ManagedHelpers.Threads 
{ 
    public class STASynchronizationContext : SynchronizationContext, IDisposable 
    { 
     private readonly Dispatcher dispatcher; 
     private object dispObj; 
     private readonly Thread mainThread; 

     public STASynchronizationContext() 
     { 
      mainThread = new Thread(MainThread) { Name = "STASynchronizationContextMainThread", IsBackground = false }; 
      mainThread.SetApartmentState(ApartmentState.STA); 
      mainThread.Start(); 

      //wait to get the main thread's dispatcher 
      while (Thread.VolatileRead(ref dispObj) == null) 
       Thread.Yield(); 

      dispatcher = dispObj as Dispatcher; 
     } 

     public override void Post(SendOrPostCallback d, object state) 
     { 
      dispatcher.BeginInvoke(d, new object[] { state }); 
     } 

     public override void Send(SendOrPostCallback d, object state) 
     { 
      dispatcher.Invoke(d, new object[] { state }); 
     } 

     private void MainThread(object param) 
     { 
      Thread.VolatileWrite(ref dispObj, Dispatcher.CurrentDispatcher); 
      Console.WriteLine("Main Thread is setup ! Id = {0}", Thread.CurrentThread.ManagedThreadId); 
      Dispatcher.Run(); 
     } 

     public void Dispose() 
     { 
      if (!dispatcher.HasShutdownStarted && !dispatcher.HasShutdownFinished) 
       dispatcher.BeginInvokeShutdown(DispatcherPriority.Normal); 

      GC.SuppressFinalize(this); 
     } 

     ~STASynchronizationContext() 
     { 
      Dispose(); 
     } 
    } 
} 
+1

這看起來不錯,而且很好,很簡單。問了幾年後真的回答並接受了嗎?!我會試試這個,但是「它工作嗎?」 – 2012-06-20 22:46:15

+0

@JonathonReinhart - 是的,它在我的開發測試期間確實有效,如果您發現任何問題,請讓我知道,以便我們改進答案。 – Bond 2012-06-21 07:06:09

8

idesign.net(在頁面上搜索自定義同步上下文)有一個SynchronizationContext,可以完成這項工作,但它更復雜,我需要它們。

+0

該鏈接已經腐爛並且通過。您是否可以包含一些**相關**代碼來創建自定義同步上下文? – IAbstract 2012-06-16 14:59:27

+0

有關代碼,請參見http://idesign.net/Downloads,對不起,我不知道版權是否允許我將其置於答案中。 – 2012-06-18 13:44:41

0

我已經Bond適應答案刪除在WPF依賴(調度),依靠的WinForms,而不是:

namespace ManagedHelpers.Threads 
    { 
    using System; 
    using System.Collections.Generic; 
    using System.Diagnostics; 
    using System.Linq; 
    using System.Text; 
    using System.Threading; 
    using System.Threading.Tasks; 
    using System.Windows.Forms; 
    using NUnit.Framework; 

    public class STASynchronizationContext : SynchronizationContext, IDisposable 
     { 
     private readonly Control control; 
     private readonly int mainThreadId; 

     public STASynchronizationContext() 
     { 
     this.control = new Control(); 

     this.control.CreateControl(); 

     this.mainThreadId = Thread.CurrentThread.ManagedThreadId; 

     if (Thread.CurrentThread.Name == null) 
      { 
      Thread.CurrentThread.Name = "AsynchronousTestRunner Main Thread"; 
      } 
     } 

     public override void Post(SendOrPostCallback d, object state) 
     { 
     control.BeginInvoke(d, new object[] { state }); 
     } 

     public override void Send(SendOrPostCallback d, object state) 
     { 
     control.Invoke(d, new object[] { state }); 
     } 

     public void Dispose() 
     { 
     Assert.AreEqual(this.mainThreadId, Thread.CurrentThread.ManagedThreadId); 

     this.Dispose(true); 
     GC.SuppressFinalize(this); 
     } 

     protected virtual void Dispose(bool disposing) 
     { 
     Assert.AreEqual(this.mainThreadId, Thread.CurrentThread.ManagedThreadId); 

     if (disposing) 
      { 
      if (control != null) 
       { 
       control.Dispose(); 
       } 
      } 
     } 

     ~STASynchronizationContext() 
     { 
     this.Dispose(false); 
     } 
     } 
    } 
2

也有類似的規定 - 單元測試服務器組件,以確認它的回調委託調用被整理到一個合適的SynchronizationContext並與弗洛想出了(基於Stephen Toub的博客文章http://blogs.msdn.com/b/pfxteam/archive/2012/01/20/10259049.aspx),因爲我使用自己的內部線程來服務Post()/Send()請求,而不是依靠WPF/Winforms/..來執行調度,所以我重新調整更簡單和更通用。

// A simple SynchronizationContext that encapsulates it's own dedicated task queue and processing 
    // thread for servicing Send() & Post() calls. 
    // Based upon http://blogs.msdn.com/b/pfxteam/archive/2012/01/20/10259049.aspx but uses it's own thread 
    // rather than running on the thread that it's instanciated on 
    public sealed class DedicatedThreadSynchronisationContext : SynchronizationContext, IDisposable 
    { 
     public DedicatedThreadSynchronisationContext() 
     { 
      m_thread = new Thread(ThreadWorkerDelegate); 
      m_thread.Start(this); 
     } 

     public void Dispose() 
     { 
      m_queue.CompleteAdding(); 
     } 

     /// <summary>Dispatches an asynchronous message to the synchronization context.</summary> 
     /// <param name="d">The System.Threading.SendOrPostCallback delegate to call.</param> 
     /// <param name="state">The object passed to the delegate.</param> 
     public override void Post(SendOrPostCallback d, object state) 
     { 
      if (d == null) throw new ArgumentNullException("d"); 
      m_queue.Add(new KeyValuePair<SendOrPostCallback, object>(d, state)); 
     } 

     /// <summary> As 
     public override void Send(SendOrPostCallback d, object state) 
     { 
      using (var handledEvent = new ManualResetEvent(false)) 
      { 
       Post(SendOrPostCallback_BlockingWrapper, Tuple.Create(d, state, handledEvent)); 
       handledEvent.WaitOne(); 
      } 
     } 

     public int WorkerThreadId { get { return m_thread.ManagedThreadId; } } 
     //========================================================================================= 

     private static void SendOrPostCallback_BlockingWrapper(object state) 
     { 
      var innerCallback = (state as Tuple<SendOrPostCallback, object, ManualResetEvent>); 
      try 
      { 
       innerCallback.Item1(innerCallback.Item2); 
      } 
      finally 
      { 
       innerCallback.Item3.Set(); 
      } 
     } 

     /// <summary>The queue of work items.</summary> 
     private readonly BlockingCollection<KeyValuePair<SendOrPostCallback, object>> m_queue = 
      new BlockingCollection<KeyValuePair<SendOrPostCallback, object>>(); 

     private readonly Thread m_thread = null; 

     /// <summary>Runs an loop to process all queued work items.</summary> 
     private void ThreadWorkerDelegate(object obj) 
     { 
      SynchronizationContext.SetSynchronizationContext(obj as SynchronizationContext); 

      try 
      { 
       foreach (var workItem in m_queue.GetConsumingEnumerable()) 
        workItem.Key(workItem.Value); 
      } 
      catch (ObjectDisposedException) { } 
     } 
    }