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

यह बहुत अच्छा और अच्छा और सरल दिखता है। क्या यह वास्तव में उत्तर दिया गया था और पूछे जाने के लगभग 3 साल बाद स्वीकार किया ?! मैं इसे आजमाने की कोशिश कर रहा हूं, लेकिन "क्या यह काम करता है?" –

+0

@ जोनाथन रेनहार्ट - हाँ, यह मेरे देव परीक्षण के दौरान काम करता है, अगर आपको इसके साथ कोई समस्या मिलती है तो कृपया मुझे बताएं ताकि हम जवाब में सुधार कर सकें। – Bond

8

idesign.net (पृष्ठ पर कस्टम सिंक्रनाइज़ेशन संदर्भ की खोज) में सिंक्रनाइज़ेशन कॉन्टेक्स्ट है जो नौकरी करेगा, हालांकि यह मुझे अधिक जटिल है।

+0

लिंक तब से और उसके माध्यम से रुक गया है। क्या आप संभवतः कस्टम सिंक संदर्भ बनाने के लिए ** ** ** ** में से कुछ को शामिल कर सकते हैं? – IAbstract

+0

कोड के लिए http://idesign.net/Downloads देखें, क्षमा करें मुझे नहीं पता कि प्रतिलिपि सही मुझे जवाब में रखने की अनुमति देगी या नहीं। –

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 पर मार्शल थे और follo के साथ आया था विंग कोड (स्टीफन टब के ब्लॉग पोस्ट 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) { } 
     } 
    } 
संबंधित मुद्दे