Looking for an example of a custom SynchronizationContext (Required for unit testing)

后端 未结 4 718
悲哀的现实
悲哀的现实 2020-12-03 01:50

I need a custom SynchronizationContext that:

  • Owns a single thread that runs \"Posts\" and \"Sends\" delegates
  • Does the send in the order they are sen
相关标签:
4条回答
  • 2020-12-03 02:03

    Had a similar requirement - unit testing a server component to confirm that it's callback delegate invocations were marshalled onto an appropriate SynchronizationContext and came up with the following code (based on Stephen Toub's blog post http://blogs.msdn.com/b/pfxteam/archive/2012/01/20/10259049.aspx) which I recon is simpler and more general as it uses it's own internal thread to service the Post()/Send() requests, rather than relying on WPF/Winforms/.. to perform dispatching.

        // 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) { }
            }
        }   
    
    0 讨论(0)
  • 2020-12-03 02:12

    idesign.net (search for Custom Synchronization Context on the page) has a SynchronizationContext that will do the job, however it is more complex them I need.

    0 讨论(0)
  • 2020-12-03 02:18

    I have adapted the answer by Bond to remove the dependency on WPF (Dispatcher), and depend on WinForms instead:

    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);
             }
          }
       }
    
    0 讨论(0)
  • 2020-12-03 02:25

    This one was written by me some time ago, no issues with copyright, no guarantees either(the system didn't go into production):

    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();
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题