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
Related
I have a C# Windows IoT Background application I have created. That application has multiple threads in the ThreadPool that run indefinitely.
These threads need to be able to read/write to global variables in the main thread, but I am not sure how to accomplish this. Here is an example of what I am trying to do:
// main task
public sealed class StartupTask : IBackgroundTask
{
private static BackgroundTaskDeferral _Deferral = null;
private static MyThreadClass1 thread1 = null;
private static MyThreadClass2 thread2 = null;
private static MyThreadClass3 thread3 = null;
List<Object> MyDevices = null;
public async void Run(IBackgroundTaskInstance taskInstance)
{
_Deferral = taskInstance.GetDeferral();
MyDevices = GetDeviceList();
thread1 = new MyThreadClass1();
await ThreadPool.RunAsync(workItem =>
{
thread1.Start();
});
thread2 = new MyThreadClass2();
await ThreadPool.RunAsync(workItem =>
{
thread2.Start();
});
thread3 = new MyThreadClass3();
await ThreadPool.RunAsync(workItem =>
{
thread3.Start();
});
}
}
internal class MyThreadClass1
{
public async void Start()
{ }
}
internal class MyThreadClass2
{
public async void Start()
{ }
}
internal class MyThreadClass3
{
public async void Start()
{ }
}
In any of the three threads that are running, I need to be able to read and write to List<Object> MyDevices.
The threads all have different functions, but they all interact with "MyDevices", so if one thread makes a change to that list, the other threads need to know about the change right away.
What is the best way to go about doing this?
Thanks!
These threads need to be able to read/write to global variables in the main thread
The easiest way to deal with this requirement is to remove it. Is it possible to code the solution so that each thread owns a device? Or is it possible to rethink the thread's responsibilities so that they communicate by message passing instead of updating shared data? Usually these alternative approaches result in much cleaner and less buggy code. But not always.
You will need locks to protect shared data. The easiest way to do this is with the lock statement, e.g.:
object _mutex = new object();
List<Object> MyDevices = null;
...
var device = ...;
lock (_mutex)
{
MyDevices.Add(device);
}
Generally, you want to minimize the code in the lock statement. Also, you may want to have one lock for the List<Object> and a separate lock for each item in the list, depending on how your thread use those devices.
One thing you might want to consider using is an ObservableCollection. This class implements the INotifyPropertyChanged interface, which notifies any listeners of changes to the underlying collection.
Next, you'll want to implement an event handler for PropertyChanged in your Thread classes like so (I recommend making either an interface or base class that handles this since you appear to be using different classes for each Thread):
public sealed class MyThreadBase
{
private ObservableCollection<object> MyDevices;
public MyThreadBase(ObservableCollection<object> deviceList)
{
MyDevices = deviceList;
MyDevices.PropertyChanged += MyDevices_PropertyChanged; // Register listener
}
private void MyDevices_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
lock (MyDevices)
{
// Do something with the data...
}
}
}
The lock statement is used so that the thread is blocked when another thread is reading or writing to MyDevices. This is typically important in synchronization and is known as the readers-writers problem. I'd suggest reading up on that and possible solutions as well.
If you intend, however, for each thread to iterate over the devices and do something with each one, then you will run into issues, as iterating over a changing collection is not a good idea (and when using a foreach loop, will actually throw an exception), so keep that in mind too.
other threads need to know about the change right away
If you want low latency notifications, threads must spend the majority of time sleeping on something. E.g. executing Dispatcher.Run() that will sleep waiting for messages/tasks to process.
If that’s your case, you can use ObservableCollection instead of List, and write CollectionChanged handler that forwards notifications for your 3 threads. Or if that’s what you want, forward notifications to 2 other threads, excluding the current one, if you won’t want the thread that initiated the change to process changed event.
I'm not sure if the Dispatcher class is available on Windows IoT platform. Definitely not the case for .NET core. Even if not, high-level building blocks to create one are available. Here’s an example implementation that also implements synchronization context, very simple because relies on high-level ConcurrentQueue and BlockingCollection generic classes.
using kvp = KeyValuePair<SendOrPostCallback, object>;
enum eShutdownReason : byte
{
Completed,
Failed,
Unexpected,
}
class Dispatcher : IDisposable
{
const int maxQueueLength = 100;
readonly ConcurrentQueue<kvp> m_queue;
readonly BlockingCollection<kvp> m_block;
public Dispatcher()
{
m_queue = new ConcurrentQueue<kvp>();
m_block = new BlockingCollection<kvp>( m_queue, maxQueueLength );
createdThreadId = Thread.CurrentThread.ManagedThreadId;
prevContext = SynchronizationContext.Current;
SynchronizationContext.SetSynchronizationContext( new SyncContext( this ) );
}
readonly SynchronizationContext prevContext;
readonly int createdThreadId;
class SyncContext : SynchronizationContext
{
readonly Dispatcher dispatcher;
public SyncContext( Dispatcher dispatcher )
{
this.dispatcher = dispatcher;
}
// https://blogs.msdn.microsoft.com/pfxteam/2012/01/20/await-synchronizationcontext-and-console-apps/
public override void Post( SendOrPostCallback cb, object state )
{
dispatcher.Post( cb, state );
}
}
/// <summary>Run the dispatcher. Must be called on the same thread that constructed the object.</summary>
public eShutdownReason Run()
{
Debug.Assert( Thread.CurrentThread.ManagedThreadId == createdThreadId );
while( true )
{
kvp h;
try
{
h = m_block.Take();
}
catch( Exception ex )
{
ex.logError( "Dispatcher crashed" );
return eShutdownReason.Unexpected;
}
if( null == h.Key )
return (eShutdownReason)h.Value;
try
{
h.Key( h.Value );
}
catch( Exception ex )
{
ex.logError( "Exception in Dispatcher.Run" );
}
}
}
/// <summary>Signal dispatcher to shut down. Can be called from any thread.</summary>
public void Stop( eShutdownReason why )
{
Logger.Info( "Shutting down, because {0}", why );
Post( null, why );
}
/// <summary>Post a callback to the queue. Can be called from any thread.</summary>
public void Post( SendOrPostCallback cb, object state = null )
{
if( !m_block.TryAdd( new kvp( cb, state ) ) )
throw new ApplicationException( "Unable to post a callback to the dispatcher: the dispatcher queue is full" );
}
void IDisposable.Dispose()
{
Debug.Assert( Thread.CurrentThread.ManagedThreadId == createdThreadId );
SynchronizationContext.SetSynchronizationContext( prevContext );
}
}
Regardless on whether you’ll use built-in Dispatcher or my custom one, all threads must call it’s Run method, then use asynchronous posted tasks, or async methods, to run code within the dispatcher.
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);
}));
}
}
Sorry I still don't understand how UI works and what is Dispatcher
I have such DispatchingWcfModel:
public interface IWcfModel
{
List<ConsoleData> DataList { get; set; }
event Action<List<ConsoleData>> DataArrived;
}
class DispatchingWcfModel : IWcfModel
{
private readonly IWcfModel _underlying;
private readonly Dispatcher _currentDispatcher;
public DispatchingWcfModel(IWcfModel model)
{
_currentDispatcher = Dispatcher.CurrentDispatcher;
_underlying = model;
_underlying.DataArrived += _underlying_DataArrived;
}
private void _underlying_DataArrived(List<ConsoleData> obj)
{
Action dispatchAction = () =>
{
if (DataArrived != null)
{
DataArrived(obj);
}
};
_currentDispatcher.BeginInvoke(DispatcherPriority.DataBind, dispatchAction);
}
public List<ConsoleData> DataList
{
get { throw new NotImplementedException(); }
set { throw new NotImplementedException(); }
}
public event Action<List<ConsoleData>> DataArrived;
}
Now I want to add int[] ConnectionStats { get; set; }. Should I introduce separate event for it? What should I write in DispatchingWcfModel? I want to have interface like that:
public interface IWcfModel
{
List<ConsoleData> DataList { get; set; }
int[] ConnectionStats { get; set; }
event Action<List<ConsoleData>> DataArrived;
}
The Dispatcher is WPF's internal message queue for the main UI Thread. It can be used from other threads to run commands on the main UI thread of an application.
This is important because WPF doesn't let you access objects which were created on other threads. For example, if a Button is created on the main UI thread, then you cannot modify this button from another thread, but you can use the Dispatcher from another thread to send a command to the main UI thread to update the button.
This applies to all objects, not just UI Elements. If something like an ObservableCollection is created on one thread, another thread cannot modify it. Because of this, all objects are usually created on the main UI thread.
Dispatcher messages can get processed synchronously or asynchronously, and they can have different priorities . In your code, you're using _currentDispatcher.BeginInvoke(DispatcherPriority.DataBind, dispatchAction);, which means it will begin an asynchronous operation with the main UI thread, and run it at the same priority as DataBinding. You can view more information on DispatcherPriorities here.
If your ConnectionStats collection gets its data asynchronously, then you will want to add some kind of DataArrived method that will take data obtained from a non-UI thread and add it to the collection. You can use the existing DataArrived method if the data is obtained and packaged together, or create your own if the data is obtained separately. If it gets it's data synchronously, you don't need to do anything special.
From the looks of things, _underlying_DataArrived is meant to run on a background thread (meaning it can't alter your collections, which should be created on the main UI thread), while DataArrived is meant to run on the main UI thread.
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
System.Collections.Concurrent has some new collections that work very well in multithreaded environments. However, they are a bit limited. Either they block until an item becomes available, or they return default(T) (TryXXX methods).
I'm needing a collection that is thread safe, but instead of blocking the calling thread it uses a callback to inform me that at least one item is available.
My current solution is to use a BlockingCollection, but to use the APM with a delegate to get the next element. In other words, I create a delegate to a method that Takes from the collection, and execute that delegate using BeginInvoke.
Unfortunately, I have to keep a lot of state within my class in order to accomplish this. Worse, the class is not thread safe; it can only be used by a single thread. I'm skirting the edge of maintainability, which I'd prefer not to do.
I know there are some libraries out there that make what I'm doing here pretty simple (I believe the Reactive Framework is one of these), but I'd like to accomplish my goals without adding any references outside of version 4 of the framework.
Are there any better patterns I can use that don't require outside references that accomplish my goal?
tl;dr:
Are there any patterns that satisfy the requirement:
"I need to signal a collection that I am ready for the next element, and have the collection execute a callback when that next element has arrived, without any threads being blocked."
I think I have two possible solutions. I am not particularly satisfied with either, but they do at least provide a reasonable alternative to the APM approach.
The first does not meet your requirement of no blocking thread, but I think it is rather elegant because you can register callbacks and they will get called in round-robin fashion, but you still have the ability to call Take or TryTake as you normally would for a BlockingCollection. This code forces callbacks to be registered each time an item is requested. That is the signalling mechanism for the collection. The nice thing about this approach is that calls to Take do not get starved as they do in my second solution.
public class NotifyingBlockingCollection<T> : BlockingCollection<T>
{
private Thread m_Notifier;
private BlockingCollection<Action<T>> m_Callbacks = new BlockingCollection<Action<T>>();
public NotifyingBlockingCollection()
{
m_Notifier = new Thread(Notify);
m_Notifier.IsBackground = true;
m_Notifier.Start();
}
private void Notify()
{
while (true)
{
Action<T> callback = m_Callbacks.Take();
T item = Take();
callback.BeginInvoke(item, null, null); // Transfer to the thread pool.
}
}
public void RegisterForTake(Action<T> callback)
{
m_Callbacks.Add(callback);
}
}
The second does meet your requirement of no blocking thread. Notice how it transfers the invocation of the callback to the thread pool. I did this because I am thinking that if it got executed synchronously then the locks would be held longer resulting in the bottlenecking of Add and RegisterForTake. I have looked it over closely and I do not think it can get live locked (both an item and a callback are available, but the callback never gets executed) but you might want to look it over yourself to verify. The only problem here is that a call to Take would get starved as callbacks always take priority.
public class NotifyingBlockingCollection<T>
{
private BlockingCollection<T> m_Items = new BlockingCollection<T>();
private Queue<Action<T>> m_Callbacks = new Queue<Action<T>>();
public NotifyingBlockingCollection()
{
}
public void Add(T item)
{
lock (m_Callbacks)
{
if (m_Callbacks.Count > 0)
{
Action<T> callback = m_Callbacks.Dequeue();
callback.BeginInvoke(item, null, null); // Transfer to the thread pool.
}
else
{
m_Items.Add(item);
}
}
}
public T Take()
{
return m_Items.Take();
}
public void RegisterForTake(Action<T> callback)
{
lock (m_Callbacks)
{
T item;
if (m_Items.TryTake(out item))
{
callback.BeginInvoke(item, null, null); // Transfer to the thread pool.
}
else
{
m_Callbacks.Enqueue(callback);
}
}
}
}
How about something like this? (The naming could probably use some work. And note that this is untested.)
public class CallbackCollection<T>
{
// Sychronization object to prevent race conditions.
private object _SyncObject = new object();
// A queue for callbacks that are waiting for items.
private ConcurrentQueue<Action<T>> _Callbacks = new ConcurrentQueue<Action<T>>();
// A queue for items that are waiting for callbacks.
private ConcurrentQueue<T> _Items = new ConcurrentQueue<T>();
public void Add(T item)
{
Action<T> callback;
lock (_SyncObject)
{
// Try to get a callback. If no callback is available,
// then enqueue the item to wait for the next callback
// and return.
if (!_Callbacks.TryDequeue(out callback))
{
_Items.Enqueue(item);
return;
}
}
ExecuteCallback(callback, item);
}
public void TakeAndCallback(Action<T> callback)
{
T item;
lock(_SyncObject)
{
// Try to get an item. If no item is available, then
// enqueue the callback to wait for the next item
// and return.
if (!_Items.TryDequeue(out item))
{
_Callbacks.Enqueue(callback);
return;
}
}
ExecuteCallback(callback, item);
}
private void ExecuteCallback(Action<T> callback, T item)
{
// Use a new Task to execute the callback so that we don't
// execute it on the current thread.
Task.Factory.StartNew(() => callback.Invoke(item));
}
}