WPF Multithreading & ICollectionChanged - c#

I'm currently using .NET 3.5 for a project and I need to implement a static Queue that gets it's items enqueued by one background thread and dequeued by yet another. Since there isn't an ObservableQueue or something similar in .NET 3.5 I tried to derive my own Queue and implement INotifyCollectionChanged, since my UI should display the contents of the Queue to the user.
But when I try to run it, the first background worker enqueues an item, CollectionChanged gets raised and then I end up with an exception like
c# the calling thread cannot access this object because a different thread owns it
My business objects (those things in the queue) all implement INotifyPropertyChanged and the background worker that dequeues them also changes some properties and therefore the same thread also calls PropertyChanged along with CollectionChanged. Strangely enough I don't got any error when PropertyChanged get raised, but CollectionChanged crashes... can someone help me on this?

Have you tried to delegate when your background thread modifies the queue ?
Like :
if (Dispatcher.Thread != Thread.CurrentThread)
{
Dispatcher.Invoke(new Action(delegate()
{
//Modify your collection
}));
}

You get such an exception when you're trying to interact with GUI from non-dispatcher (i.e. non-GUI) thread. In your case I assume you're just raising CollectionChanged event without checking whether it is GUI-thread or not. In order to resolve this issue you should, as chrisendymion wrote, us Dispatcher. I see two possible options here:
1) Every call to your custom queue (add/delete) is made on Dispatcher (Dispatcher.Invoke)
2) Wrap code that raises CollectionChanged event to the same Dispatcher.Invoke.
Hope this helps.

Use something like this:
if (System.Windows.Application.Current.Dispatcher.CheckAccess())
{
Messages.Add(message);
}
else
{
System.Windows.Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal,
new Action(() => Messages.Add(message)));
}
You can only access the from GUI-Thread because it is possible that the GUI is bound to the collection. And a lot of threads working with the gui isnt that good thing because as result you will often recieve errors.
So just think about: Why do you can't do something like this: listBox1.Items.Add(...) from another thread. Now you know why you can t do access the list from another thread.
(if you really don t know why you can do something like listBox1.Items.Add(...) google for it... you will find a lot of articles

First, i thing you should extend ObservableCollection insted of implementig an INotifyCollectionChanged collection from the begining. And for multi-thread collection see this class, that may be helpful for you (only for work with multi-thread, if you want add the Queue behavior you need to implement it)
public class ThreadSafeObservableCollection : ObservableCollection
{
private SynchronizationContext SynchronizationContext;
public ThreadSafeObservableCollection()
{
SynchronizationContext = SynchronizationContext.Current;
// current synchronization context will be null if we're not in UI Thread
if (SynchronizationContext == null)
throw new InvalidOperationException("This collection must be instantiated from UI Thread, if not, you have to pass SynchronizationContext to con structor.");
}
public ThreadSafeObservableCollection(SynchronizationContext synchronizationContext)
{
if (synchronizationContext == null)
throw new ArgumentNullException("synchronizationContext");
this.SynchronizationContext = synchronizationContext;
}
protected override void ClearItems()
{
this.SynchronizationContext.Send(new SendOrPostCallback((param) => base.ClearItems()), null);
}
protected override void InsertItem(int index, T item)
{
this.SynchronizationContext.Send(new SendOrPostCallback((param) => base.InsertItem(index, item)), null);
}
protected override void RemoveItem(int index)
{
this.SynchronizationContext.Send(new SendOrPostCallback((param) => base.RemoveItem(index)), null);
}
protected override void SetItem(int index, T item)
{
this.SynchronizationContext.Send(new SendOrPostCallback((param) => base.SetItem(index, item)), null);
}
protected override void MoveItem(int oldIndex, int newIndex)
{
this.SynchronizationContext.Send(new SendOrPostCallback((param) => base.MoveItem(oldIndex, newIndex)), null);
}
}

Everyone has a good technical answer for why this isn't working. The "Dispatcher" object assigned to your current thread doesn't have access to the UI.
In .NET if a dispatcher does not exist in your current thread, .NET will new up another Dispatcher, however this will have the same synchronization context as your background thread - not the UI thread where it needs to synchronize access to.
The easiest solution for my money would be to hold a reference to the original dispatcher in the ObservableCollection, and override the base class calls in a different Dispatcher, something like this should work:
public class ObservableDispatcherCollection<T> : ObservableCollection<T> where T : class
{
private Dispatcher _dispatcher;
public ObservableDispatcherCollection(Dispatcher dispatcher)
{
_dispatcher = dispatcher;
}
public ObservableDispatcherCollection(Control parent)
{
_dispatcher = parent.Dispatcher;
}
protected override void OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
_dispatcher.Invoke(new Action(() =>
{
base.OnCollectionChanged(e);
}));
}
protected override void OnPropertyChanged(System.ComponentModel.PropertyChangedEventArgs e)
{
_dispatcher.Invoke(new Action(() =>
{
base.OnPropertyChanged(e);
}));
}
}

Related

Using BindingOperations.EnableCollectionNotifications and suspending notifications

I'm struggling with NotifyCollectionChangedAction.Reset on a separate thread.
I have the following class which overrides the ObservavbleCollection so that I can suspend notifications when doing bulk updates. In the constructor I also make a call to support modifying the collection on different thread using BindingOperations.EnableCollectionSyncronization
The issue I have is when doing the NotifyCollectionChangedAction.Reset not on the UI thread I get an exception (regular Add/Remove to the collection work). I thought BindingOperations.EnableCollectionSyncronization enabled cross thread notifications?
public class ObservableDataCollection<T> : ObservableCollection<T>
{
private bool _suppressNotification = false;
private object _lock = new object();
public ObservableDataCollection(IEnumerable<T> collection) : base(collection) { BindingOperations.EnableCollectionSynchronization(Items, _lock); }
public ObservableDataCollection() { BindingOperations.EnableCollectionSynchronization(Items, _lock); }
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (!_suppressNotification)
base.OnCollectionChanged(e);
}
protected override void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (!_suppressNotification)
base.OnPropertyChanged(e);
}
public void SuppressNotifications(bool suppressNotification)
{
_suppressNotification = suppressNotification;
if (_suppressNotification == false)
base.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
Then when I make changes
// On UI thread
var synchronizedCollection = new ObservableDataCollection();
BindingOperations.EnableCollectionSynchronization(synchronizedCollection, synchronizedCollection.SyncLock);
// Background thread
syncronizedCollection.SupressNotifications(true);
synchronizedCollection.Clear();
synchronizedCollection.Add/Remove etc
syncronizedCollection.SupressNotifications(false); // throws
I had assumed that BindingOperations.EnableCollectionSynchronization would take care of dispatching base.OnCollectionChanged in my SupressNotifications call on the UI thread.
So I need to take care of dispatching it on the UI thread.
I know I have to call BindingOperations.EnableCollectionSynchronization` from each UI thread.
The bigger question then becomes what happens and how do I manage notifications when the collection is bound on multiple UI threads?
As I have said before, the method BindingOperations.EnableCollectionSynchronization must be called on the thread that the CollectionView of ObservableDataCollection is associated with.
CollectionView has Dispatcher affinity, which is the root of all problems and the reason you must marshal theINotifyCollectionChanged handler invocation to the correct Dispatcher thread. Taking care of this should fix your problem.
The fact that you are calling BindingOperations.EnableCollectionSynchronization in the constructor may also be a reason that lead to the issue.
Consider a scenario, where the instance of the collection is created on a different thread than it is actually used: you are going to experience a cross-thread exception. It's best practice to let the user of your class handle this.
Another important point is that BindingOperations.EnableCollectionSynchronization is only applied to the CollectionView of the collection passed as the argument. This means you are currently synchronizing the view of the internal collection Items. This is not the view that is returned when setting up a Binding that has the owning ObservableDataCollection as Binding.Source. You are synchronizing the wrong collection view and must synchronize ObservableDataCollection instead:
public ObservableDataCollection()
=> BindingOperations.EnableCollectionSynchronization(this, _lock);
But since you should remove the call to BindingOperations.EnableCollectionSynchronization from the constructor and should expose the sync lock object:
public class ObservableDataCollection<T> : ObservableCollection<T>
{
public object SyncLock { get; } = new object();
private bool _suppressNotification = false;
public ObservableDataCollection(IEnumerable<T> collection) : base(collection) {}
public ObservableDataCollection() {}
}
, the proper way to mark the collection as synchronized would be:
// On UI thread
var synchronizedCollection = new ObservableDataCollection();
BindingOperations.EnableCollectionSynchronization(synchronizedCollection, synchronizedCollection.SyncLock);
// Background thread
synchronizedCollection.Clear(); // Won't throw

Is there a way to build up a TreeView control on background thread?

I have a TreeView control that I need to populate with a large 3 tiered list of objects which is taking an incredible amount of time to build up. I'm doing the loading of the data on the background thread then sending the GUI updates over to the GUI thread, but there are just too many updates, for every time I add a node I have to send that across, then I have to call the ExpandSubTree() method to then expand all the sub nodes, which then fires off more expand events, and it crashes.
Is there a way I can build up the control and it's open/closed state somehow on a background thread and then only marshall it over once it's complete?
Each tree view item had a property Children, if you bind each Tree View Item's Children to an ObservableCollection you may add item to it from a BackGroundWorker or another thread. If you use the follow collection to bind the tree view item children you may add-remove children from background to the view. It use a synchronization context to add items to the view:
public class ThreadSafeObservableCollection<T> : ObservableCollection<T>
{
private SynchronizationContext SynchronizationContext;
public ThreadSafeObservableCollection()
{
SynchronizationContext = SynchronizationContext.Current;
// current synchronization context will be null if we're not in UI Thread
if (SynchronizationContext == null)
throw new InvalidOperationException("This collection must be instantiated from UI Thread, if not, you have to pass SynchronizationContext to con structor.");
}
public ThreadSafeObservableCollection(SynchronizationContext synchronizationContext)
{
if (synchronizationContext == null)
throw new ArgumentNullException("synchronizationContext");
this.SynchronizationContext = synchronizationContext;
}
protected override void ClearItems()
{
this.SynchronizationContext.Send(new SendOrPostCallback((param) => base.ClearItems()), null);
}
protected override void InsertItem(int index, T item)
{
this.SynchronizationContext.Send(new SendOrPostCallback((param) => base.InsertItem(index, item)), null);
}
protected override void RemoveItem(int index)
{
this.SynchronizationContext.Send(new SendOrPostCallback((param) => base.RemoveItem(index)), null);
}
protected override void SetItem(int index, T item)
{
this.SynchronizationContext.Send(new SendOrPostCallback((param) => base.SetItem(index, item)), null);
}
protected override void MoveItem(int oldIndex, int newIndex)
{
this.SynchronizationContext.Send(new SendOrPostCallback((param) => base.MoveItem(oldIndex, newIndex)), null);
}
}
Also i think that this articles must be useful to you:
Simplifying the WPF TreeView by Using the ViewModel Pattern
Custom TreeView Layout in WPF
Hope this would be useful for you...
Are you creating the entire tree all at once?
Are you firing invokes for each item that is created?
I would think about loading the tree on-demand. Maybe when a user goes to expand a node, you handle that event and go get the data. I would also consider loading groups of items per invoke

Detecting whether on UI thread in WPF and Winforms

I've written an assertion method Ensure.CurrentlyOnUiThread(), below, that checks that the current thread is a UI thread.
Is this going to be reliable in detecting the Winforms UI thread?
Our app is mixed WPF and Winforms, how best to detect a valid WPF UI thread?
Is there a better way to do this? Perhaps code contracts?
Ensure.cs
using System.Diagnostics;
using System.Windows.Forms;
public static class Ensure
{
[Conditional("DEBUG")]
public static void CurrentlyOnUiThread()
{
if (!Application.MessageLoop)
{
throw new ThreadStateException("Assertion failed: not on the UI thread");
}
}
}
Don't use
if(Dispatcher.CurrentDispatcher.Thread == Thread.CurrentThread)
{
// Do something
}
Dispatcher.CurrentDispatcher will, if the current thread do not have a dispatcher, create and return a new Dispatcher associated with the current thread.
Instead do like this
Dispatcher dispatcher = Dispatcher.FromThread(Thread.CurrentThread);
if (dispatcher != null)
{
// We know the thread have a dispatcher that we can use.
}
To be sure you have the correct dispatcher or are on the correct thread you have the following options
Dispatcher _myDispatcher;
public void UnknownThreadCalling()
{
if (_myDispatcher.CheckAccess())
{
// Calling thread is associated with the Dispatcher
}
try
{
_myDispatcher.VerifyAccess();
// Calling thread is associated with the Dispatcher
}
catch (InvalidOperationException)
{
// Thread can't use dispatcher
}
}
CheckAccess() and VerifyAccess() do not show up in intellisense.
Also, if you have to resort to these kinds of things its likely due to bad design. You should know which threads run what code in your program.
For WPF, I use the following:
public static void InvokeIfNecessary (Action action)
{
if (Thread.CurrentThread == Application.Current.Dispatcher.Thread)
action ();
else {
Application.Current.Dispatcher.Invoke(action);
}
}
The key is instead of checking Dispatcher.CurrentDispatcher (which will give you the dispatcher for the current thread), you need to check if the current thread matches the dispatcher of the application or another control.
Within WinForms you would normally use
if(control.InvokeRequired)
{
// Do non UI thread stuff
}
for WPF
if (!control.Dispatcher.CheckAccess())
{
// Do non UI Thread stuff
}
I would probably write a little method that uses a Generic constraint to determine which of these you should be calling. e.g.
public static bool CurrentlyOnUiThread<T>(T control)
{
if(T is System.Windows.Forms.Control)
{
System.Windows.Forms.Control c = control as System.Windows.Forms.Control;
return !c.InvokeRequired;
}
else if(T is System.Windows.Controls.Control)
{
System.Windows.Controls.Control c = control as System.Windows.Control.Control;
return c.Dispatcher.CheckAccess()
}
}
For WPF:
// You are on WPF UI thread!
if (Thread.CurrentThread == System.Windows.Threading.Dispatcher.CurrentDispatcher.Thread)
For WinForms:
// You are NOT on WinForms UI thread for this control!
if (someControlOrWindow.InvokeRequired)
Maybe Control.InvokeRequired (WinForms) and Dispatcher.CheckAccess (WPF) are OK for you?
You're pushing knowledge of your UI down into your logic. This is not a good design.
Your UI layer should be handling threading, as ensuring the UI thread isn't abused is within the purview of the UI.
This also allows you to use IsInvokeRequired in winforms and Dispatcher.Invoke in WPF... and allows you to use your code within synchronous and asynchronous asp.net requests as well...
I've found in practice that trying to handle threading at a lower level within your application logic often adds lots of unneeded complexity. In fact, practically the entire framework is written with this point conceded--almost nothing in the framework is thread safe. Its up to callers (at a higher level) to ensure thread safety.
Here is a snippet of code I use in WPF to catch attempts to modify UI Properties (that implement INotifyPropertyChanged) from a non-UI thread:
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
// Uncomment this to catch attempts to modify UI properties from a non-UI thread
//bool oopsie = false;
//if (Thread.CurrentThread != Application.Current.Dispatcher.Thread)
//{
// oopsie = true; // place to set a breakpt
//}
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
For WPF:
I've needed to know is Dispatcher on my thread is actually started, or not. Because if you create any WPF class on the thread, the accepted answer will state that the dispatcher is there, even if you never do the Dispatcher.Run(). I've ended up with some reflection:
public static class WpfDispatcherUtils
{
private static readonly Type dispatcherType = typeof(Dispatcher);
private static readonly FieldInfo frameDepthField = dispatcherType.GetField("_frameDepth", BindingFlags.Instance | BindingFlags.NonPublic);
public static bool IsInsideDispatcher()
{
// get dispatcher for current thread
Dispatcher currentThreadDispatcher = Dispatcher.FromThread(Thread.CurrentThread);
if (currentThreadDispatcher == null)
{
// no dispatcher for current thread, we're definitely outside
return false;
}
// get current dispatcher frame depth
int currentFrameDepth = (int) frameDepthField.GetValue(currentThreadDispatcher);
return currentFrameDepth != 0;
}
}
You can compare thread ids like this :
var managedThreadId = System.Windows.Threading.Dispatcher.FromThread(System.Threading.Thread.CurrentThread)?.Thread.ManagedThreadId;
var dispatcherManagedThreadId = System.Windows.Application.Current.Dispatcher.Thread.ManagedThreadId;
if (managedThreadId == dispatcherManagedThreadId)
{
//works in ui dispatcher thread
}
Using MVVM it is actually fairly easy. What I do is put something like the following in, say, ViewModelBase...
protected readonly SynchronizationContext SyncContext = SynchronizationContext.Current;
or...
protected readonly TaskScheduler Scheduler = TaskScheduler.Current;
Then when a particular ViewModel needs to touch anything "observable", you can check the context and react accordingly...
public void RefreshData(object state = null /* for direct calls */)
{
if (SyncContext != SynchronizationContext.Current)
{
SyncContext.Post(RefreshData, null); // SendOrPostCallback
return;
}
// ...
}
or do something else in the background before returning to context ...
public void RefreshData()
{
Task<MyData>.Factory.StartNew(() => GetData())
.ContinueWith(t => {/* Do something with t.Result */}, Scheduler);
}
Normally, if you follow MVVM (or any other architecture) in an orderly fashion, it is easy to tell where the responsibility for UI synchronization will be situated. But you can basically do this anywhere to return to the context where your objects are created. I'm sure it would be easy to create a "Guard" to handle this cleanly and consistently in a large and complex system.
I think it makes sense to say that your only responsibility is to get back to your own original context. It is a client's responsibility to do the same.
FOR WPF:
Here's a snippet based on the top answer, using a delegate meaning it is very generic.
/// <summary>
/// Invokes the Delegate directly on the main UI thread, based on the calling threads' <see cref="Dispatcher"/>.
/// NOTE this is a blocking call.
/// </summary>
/// <param name="method">Method to invoke on the Main ui thread</param>
/// <param name="args">Argumens to pass to the method</param>
/// <returns>The return object of the called object, which can be null.</returns>
private object InvokeForUiIfNeeded(Delegate method, params object[] args)
{
if (method == null) throw new ArgumentNullException(nameof(method));
var dispatcher = Application.Current.Dispatcher;
if (dispatcher.Thread != Thread.CurrentThread)
{
// We're on some other thread, Invoke it directly on the main ui thread.
return dispatcher.Invoke(method, args);
}
else
{
// We're on the dispatchers' thread, which (in wpf) is the main UI thread.
// We can safely update ui here, and not going through the dispatcher which safes some (minor) overhead.
return method.DynamicInvoke(args);
}
}
/// <inheritdoc cref="InvokeForUiIfNeeded(Delegate, object[])"/>
public TReturn InvokeForUiIfNeeded<TReturn>(Delegate method, params object[] args)
=> (TReturn) InvokeForUiIfNeeded(method, args);
The second method allows for a more type safe return type.
I've also added some overloads that automatically take the Func and Action parameters in my code, e.g:
/// <inheritdoc cref="InvokeForUiIfNeeded(System.Delegate, object[])"/>
private void InvokeForUiIfNeeded(Action action)
=> InvokeForUiIfNeeded((Delegate) action);
Note; the Func and Action inherit from Delegate so we can just cast it.
You could also add your own generic overloads that take actions, i did not bother creating a bunch of overloads but you definitely could e.g;
/// <inheritdoc cref="InvokeForUiIfNeeded(System.Delegate, object[])"/>
private void InvokeForUiIfNeeded<T1>(Action<T1> action, T1 p1)
=> InvokeForUiIfNeeded((Delegate)action, p1);
/// <inheritdoc cref="InvokeForUiIfNeeded(System.Delegate, object[])"/>
private TReturn InvokeForUiIfNeeded<T1, TReturn>(Func<T1, TReturn> action, T1 p1)
=> (TReturn)InvokeForUiIfNeeded((Delegate)action, p1);
Thread.CurrentThread.ManagedThreadId == Dispatcher.Thread.ManagedThreadId
Is a better way to check this

Invoking method on thread which created object

Let's say I created an object O on the thread T. How can I get, from inside object O the thread T and invoke a method on that thread?. This way, it won't be necessary for the form that created the object to to this:
private void ChangeProgress(int value)
{
progressBar1.Value = value;
}
void FD_ProgressChanged(object sender, DownloadEventArgs e)
{
if (InvokeRequired)
{
Invoke(new Action<int>(ChangeProgress), new object[] { e.PercentDone });
}
else ChangeProgress(e.PercentDone);
}
which is just ugly and requires whoever uses the object to either figure out which events are raised on the same thread that created the object and which are not and add the if(InvokeRequired)...else code on the ones that are not, or just add the code on every single event handler. I think it would be more elegant if the object itself takes care of invoking the event on the right thread. Is this possible?
Use the BackgroundWorker class. It takes care of all this. Note the ReportProgress event.
You are going to have to track it yourself like
class Foo {
private readonly Thread creatingThread;
public Foo() {
this.creatingThread = Thread.CurrentThread;
}
}
If you don't do that, there is no way to know. But the fact that you are doing this is a smell. Consider using a BackgroundWorker.
There are a few thing you need to consider:
You will need to keep a reference in Object O of a thread that it was created in. Probably in a constructor using Thread.Current static property.
That thread will need to have a SynchronizationContext associated with it. (Generally, UI threads have it. And its not easy to create one for a custom thread you created.)
To invoke a method on that thread, you will need to use Send() or Post() methods on that thread's SynchronizationContext.
Found a nice solution at http://www.codeproject.com/KB/threads/invoke_other_way.aspx
And here's is my generic version:
private void RaiseEventAsync(Delegate handler, object e)
{
if (null != handler)
{
List<Delegate> invocationList = handler.GetInvocationList().ToList();
foreach (Delegate singleCast in invocationList)
{
System.ComponentModel.ISynchronizeInvoke syncInvoke =
singleCast.Target as System.ComponentModel.ISynchronizeInvoke;
try
{
if ((null != syncInvoke) && (syncInvoke.InvokeRequired))
syncInvoke.Invoke(singleCast,
new object[] { this, e });
else
singleCast.Method.Invoke(singleCast.Target, new object[] { this, e });
}
catch
{ }
}
}
}
And this is how you would use it:
protected void OnProgressChanged(DownloadEventArgs e)
{
RaiseEventAsync(ProgressChanged, e);
}
This takes care of my problem without needing to use a BackgroundWorker that is not always wanted (like on my case, where I'm subclassing a class that already uses a different threading object).

Deriving from SynchonizationContext

In short, I've implemented a class that derives from SynchronizationContext to make it easy for GUI applications to consume events raised on threads other than the GUI thread. I'd very much appreciate comments on my implementation. Specifically, is there anything you would recommend against or that might cause problems that I haven't foreseen? My initial tests have been successful.
The long version:
I'm currently developing the business layer of a distributed system (WCF) that uses callbacks to propagate events from the server to clients. One of my design objectives is to provide bindable business objects (i.e. INotifyPropertyChanged/IEditableObject, etc.) to make it easy to consume these on the client-side. As part of this I provide an implementation of the callback interface that handles events as they come in, updates the business objects which, in turn, raise property changed events. I therefore need these events to be raised on the GUI thread (to avoid cross-thread operation exceptions). Hence my attempt at providing a custom SynchronizationContext, which is used by the class implementing the callback interface to propagate events to the GUI thread. In addition, I want this implementation to be independent of the client environment - e.g. a WinForms GUI app or a ConsoleApp or something else. In other words, I don't want to assume that the static SynchronizationContext.Current is available. Hence my use of the ExecutionContext as a fallback strategy.
public class ImplicitSynchronisationContext : SynchronizationContext
{
private readonly ExecutionContext m_ExecContext;
private readonly SynchronizationContext m_SyncContext;
public ImplicitSynchronisationContext()
{
// Default to the current sync context if available.
if (SynchronizationContext.Current != null)
{
m_SyncContext = SynchronizationContext.Current;
}
else
{
m_ExecContext = ExecutionContext.Capture();
}
}
public override void Post(SendOrPostCallback d, object state)
{
if (m_SyncContext != null)
{
m_SyncContext.Post(d, state);
}
else
{
ExecutionContext.Run(
m_ExecContext.CreateCopy(),
(object args) =>
{
ThreadPool.QueueUserWorkItem(new WaitCallback(this.Invoker), args);
},
new object[] { d, state });
}
}
public override void Send(SendOrPostCallback d, object state)
{
if (m_SyncContext != null)
{
m_SyncContext.Send(d, state);
}
else
{
ExecutionContext.Run(
m_ExecContext.CreateCopy(),
new ContextCallback(this.Invoker),
new object[] { d, state });
}
}
private void Invoker(object args)
{
Debug.Assert(args != null);
Debug.Assert(args is object[]);
object[] parts = (object[])args;
Debug.Assert(parts.Length == 2);
Debug.Assert(parts[0] is SendOrPostCallback);
SendOrPostCallback d = (parts[0] as SendOrPostCallback);
d(parts[1]);
}
}
Unfortunately you wrote something that already exists. The SynchronizationContext class does exactly what you do. Add a property to your main class, similar to this:
public static SynchronizationContext SynchronizationContext {
get {
if (SynchronizationContext.Current == null) {
SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
}
return SynchronizationContext.Current;
}
}
Or use AsyncOperationManager.SynchronizationContext, it does the exact same thing. Preferable of course.
I see nothing technically wrong with the code above..
However, it is more complicated than really necessary. There is no real reason to copy the ExecutionContext and run the operations within it. This happens automatically with a call to ThreadPool.QueueUserWorkItem. For details, see the docs of ExecutionContext:
Within an application domain, the entire execution context must be transferred whenever a thread is transferred. This situation occurs during transfers made by the Thread.Start method, most thread pool operations, and Windows Forms thread marshaling through the Windows message pump.
Personally, I would abandon tracking of the ExecutionContext unless there is a real need for it, and just simplify this to:
public class ImplicitSynchronisationContext : SynchronizationContext
{
private readonly SynchronizationContext m_SyncContext;
public ImplicitSynchronisationContext()
{
// Default to the current sync context if available.
m_SyncContext = SynchronizationContext.Current;
}
public override void Post(SendOrPostCallback d, object state)
{
if (m_SyncContext != null)
{
m_SyncContext.Post(d, state);
}
else
{
ThreadPool.QueueUserWorkItem(_ => d(state));
}
}
public override void Send(SendOrPostCallback d, object state)
{
if (m_SyncContext != null)
{
m_SyncContext.Send(d, state);
}
else
{
d(state);
}
}
}
I'm a little unsure about your motivation for writing this class.
If you're using WinForms or WPF, they provide implementations that are available using SynchronizationContext.Current.
If you're in a Console application, then you control the main thread. How are you communicating with this thread?
If you're running a windows message loop, presumably you are using WinForms or WPF.
If you're waiting on a producer/consumer queue, your main thread will be the one consuming the events, so by definition you will be on the main thread.
Nick
Thanks very much for the feedback everyone.
Hans Passant's answer led me to evolve/change my solution.
Just to recap, my problem was essentially how to get async callbacks from my WCF service to propagate to the UI thread of a client (WinForms or WPF) without requiring any work on the part of the client developer.
I've dropped the implementation offered above because it is redundant. My implementation of the service callback contract now simply has an overloaded constructor that takes bool synchroniseCallbacks. When it is true I store a reference to AsyncOperationManager.SynchronizationContext. When events come in from my service I post or send them using that sync context.
As Hans pointed out, the benefit if using the sync context exposed by AsyncOperationManager is that it will never be null and, also, in GUI apps such as WinForms and WPF it will return the sync context of the UI thread - problem solved!
Cheers!

Categories