I'm using a WPF CollectionView and I'm setting the Filter in a background thread, because it takes a long time to apply this filter.
Setting this Filter triggers the method ScheduleMapCleanup () of CollectionView (so WPF framework code I can't change). In this method, Dispatcher.CurrentDispatcher.BeginInvoke is used.
However, because this is executed in a background thread, this Action is never executed (the Dispatcher of this thread is never started), causing a memory leak: The Dispatcher keeps a reference to the CollectionView.
How could I work around this problem? Setting the Filter in the UI thread is not an option.
Could I start the Dispatcher myself? If so, how do I do this (Dispatcher.Run halts everything)?
I use this when I need to update some controls and binding on my UI thread from my background tasks:
Application.Current.Dispatcher.Invoke(
DispatcherPriority.Loaded,
new Action(() => {
// Code here
})
);
If it's not this can you be more specific on what you want to do on your UI thread
Accessing the current dispatcher from a background thread does not give you the UI dispatcher, it gives you a new one for the background thread.
Either call CurrentDispatcher
from the foreground thread and pass the result to the background thread, or call DependencyObject.Dispatcher
to get the dispatcher for a window or other control.
Edit: I just read the question more closely. Since you do not control the code calling CurrentDispatcher
, the only way that it will work is to call that code from the UI thread.
To be clear: I don't use Dispatcher.CurrentDispatcher in my code. This is used in the WPF framework code, so I can't change this. This code is executed in a background thread because I'm setting the Filter in a background thread. I'm setting this property in a background thread because it can take up to several minutes. Setting it in a background thread keeps the UI responsive and lets me show a loading indication to the user.
I fixed the memory leak (caused by the not-running background Dispatcher keeping a reference to the CollectionView) by adding a Shutdown to the Dispatcher and starting the dispatcher in the background thread:
//All code below is executed on a background thread
//Line below causes WPF framework to add something to Dispatcher.CurrentDispatcher queue.
view.Filter = new Predicate<Object>(actionTarget.FilterCallback);
if (Thread.CurrentThread.IsBackground && Dispatcher.CurrentDispatcher != Application.Current.Dispatcher)
{
Dispatcher.CurrentDispatcher.BeginInvokeShutdown(DispatcherPriority.Background);
Dispatcher.Run();
}
If the background thread is reused later (for example because it's a thread pool thread, started by a BackgroundWorker) you can't use BeginInvokeShutdown like in the code above: a shut down dispatcher can not be started again. In that case, use this instead of the BeginInvokeShutdown:
Dispatcher.CurrentDispatcher.BeginInvoke((Action) delegate() { Dispatcher.ExitAllFrames(); }, DispatcherPriority.Background);
This will make sure the Run() method returns, but the dispatcher can be started again later on.
Edit: As Mitch mentioned in comment below, be carefull when multiple threads can be executing the Run() at the same time. If necessary add a lock around the Run().
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.