WPF框架在后台线程中使用Dispatcher.CurrentDispatcher,导致内存泄漏 [英] WPF framework using Dispatcher.CurrentDispatcher in background thread, causing memory leak

查看:381
本文介绍了WPF框架在后台线程中使用Dispatcher.CurrentDispatcher,导致内存泄漏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用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.

设置此筛选器触发方法 ScheduleMapCleanup ()(因此我无法更改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,导致内存泄漏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆