2009-12-10 26 views
18

Tôi cần một tùy chỉnh SynchronizationContext rằng:Tìm kiếm một ví dụ về một SynchronizationContext tùy chỉnh (cần thiết cho việc kiểm tra đơn vị)

  • sở hữu một chủ đề duy nhất mà chạy "bài" và "Gửi" đại biểu
  • Liệu send theo thứ tự chúng được gửi trong
  • Không phương pháp khác là cần thiết

tôi cần điều này vì vậy tôi có thể kiểm tra đơn vị một số mã luồng đó sẽ nói chuyện với WinForm trong ứng dụng thực tế.

Trước khi tự viết, tôi đã hy vọng rằng ai đó có thể chỉ cho tôi một cách triển khai đơn giản (và nhỏ).

Trả lời

10

một này đã được viết bởi tôi một số thời gian trước đây, không có vấn đề quyền tác giả, không đảm bảo một trong hai (hệ thống đã không đi vào sản xuất):

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

Điều này có vẻ khá tốt, đẹp và đơn giản. Nó có thực sự được trả lời và chấp nhận gần 3 năm sau khi được hỏi không ?! Tôi sẽ thử cái này, nhưng "nó có hoạt động không?" –

+0

@JonathonReinhart - vâng, nó đã hoạt động trong quá trình thử nghiệm dev của tôi, nếu bạn tìm thấy bất kỳ vấn đề nào với nó, hãy cho tôi biết để chúng tôi có thể cải thiện câu trả lời. – Bond

8

idesign.net (tìm kiếm bối cảnh đồng bộ hóa tùy chỉnh trên trang) có một SynchronizationContext sẽ thực hiện công việc, tuy nhiên nó phức tạp hơn tôi cần.

+0

Liên kết này đã đi qua và xuyên suốt. Bạn có thể bao gồm một số mã ** thích hợp ** để tạo ngữ cảnh đồng bộ hóa tùy chỉnh không? – IAbstract

+0

xem http://idesign.net/Tải xuống mã, xin lỗi tôi không biết liệu quyền sao chép có cho phép tôi đặt câu trả lời hay không. –

0

Tôi đã thích nghi câu trả lời bằng cách Bond để loại bỏ các sự phụ thuộc vào WPF (dispatcher), và phụ thuộc vào WinForms thay vì:

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

đã có một yêu cầu tương tự - đơn vị thử nghiệm một thành phần máy chủ để xác nhận rằng nó gọi lại lời gọi đại biểu được marshalled vào một SynchronizationContext thích hợp và đã đưa ra follo mã cánh (dựa trên bài đăng blog của Stephen Toub http://blogs.msdn.com/b/pfxteam/archive/2012/01/20/10259049.aspx) mà tôi thu thập lại đơn giản và tổng quát hơn vì nó sử dụng chuỗi nội bộ của riêng nó để phục vụ các yêu cầu Post()/Send(), thay vì dựa vào WPF/Winforms/.. để thực hiện điều phối.

// 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) { } 
     } 
    } 
Các vấn đề liên quan