问题描述
我正在使用WPF CollectionView,并且将筛选器设置在后台线程中,因为应用该筛选器需要很长时间。
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.
设置此筛选器触发方法()(因此我无法更改WPF框架代码)。在此方法中,使用Dispatcher.CurrentDispatcher.BeginInvoke。
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.
但是,由于此操作是在后台线程中执行的,因此永远不会执行此Action(此线程的Dispatcher是从未启动),导致内存泄漏:Dispatcher保留对CollectionView的引用。
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.
如何解决此问题?不能在UI线程中设置过滤器。
How could I work around this problem? Setting the Filter in the UI thread is not an option.
我可以自己启动Dispatcher吗?如果是这样,我该怎么做(Dispatcher.Run停止所有操作)?
Could I start the Dispatcher myself? If so, how do I do this (Dispatcher.Run halts everything)?
推荐答案
要明确:我不使用我的代码中的Dispatcher.CurrentDispatcher。 WPF框架代码中使用了此代码,因此无法更改。
该代码在后台线程中执行,因为我在后台线程中设置了过滤器。我正在后台线程中设置此属性,因为它可能需要花费几分钟。将其设置在后台线程中可使UI保持响应状态,并让我向用户显示加载指示。
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.
我修复了内存泄漏(由未运行的后台Dispatcher引起)保持对CollectionView的引用),方法是在Dispatcher中添加一个Shutdown并在后台线程中启动调度程序:
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();
}
如果稍后重新使用后台线程(例如,因为它是线程池线程) ,由BackgroundWorker启动),您不能像上面的代码那样使用BeginInvokeShutdown:无法再次启动关闭的调度程序。在这种情况下,请使用此方法代替BeginInvokeShutdown:
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);
这将确保Run()方法返回,但是稍后可以再次启动分派器。
This will make sure the Run() method returns, but the dispatcher can be started again later on.
编辑:正如Mitch在下面的评论中提到的那样,当多个线程可以同时执行Run()时要当心。如有必要,在Run()周围添加一个锁。
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().
这篇关于WPF框架在后台线程中使用Dispatcher.CurrentDispatcher,导致内存泄漏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!