Looking for an example of a custom SynchronizationContext (Required for unit testing)
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(); } }}
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.
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) { } } }