2017-01-30 21 views
2

我正在使用WPF CollectionView,并在后台线程中设置Filter,因为它需要很长时间才能应用此筛选器。WPF框架在后台线程中使用Dispatcher.CurrentDispatcher,导致内存泄漏

设置此过滤器会触发CollectionView的方法ScheduleMapCleanup(),因此WPF框架代码无法更改。在此方法中,使用Dispatcher.CurrentDispatcher.BeginInvoke。

但是,因为这是在后台线程中执行的,所以从不执行此操作(该线程的调度程序从不启动),导致内存泄漏:Dispatcher保留对CollectionView的引用。

我该如何解决这个问题?在UI线程中设置筛选器不是一个选项。

我可以自己启动Dispatcher吗?如果是这样,我该如何做(Dispatcher.Run停止一切)?

+1

你可以添加一些你的代码?你如何做? – vendettamit

+0

没有太多代码需要显示。我只是在做collectionView.Filter = new Predicate (MyFunction);在后台线程中。 这会触发ScheduleMapCleanup()方法,您可以在这里找到:https://referencesource.microsoft.com/PresentationFramework/src/Framework/MS/Internal/Data/CollectionViewGroupInternal.cs.html#https://referencesource.microsoft .com/PresentationFramework/src/Framework/MS/Internal/Data/CollectionViewGroupInternal.cs.html,88fffd8bbbb41f50,引用 使用Dispatcher.CurrentDispatcher。哪个没有运行。 – Coder14

+0

尽管你的'在UI线程中设置过滤器不是一个选项',你是否尝试过,发生了什么?应用过滤器不应该阻止用户界面。 “ –

回答

0

要清楚:我没有在我的代码中使用Dispatcher.CurrentDispatcher。这用在WPF框架代码中,所以我不能改变它。 此代码在后台线程中执行,因为我在后台线程中设置了过滤器。我在后台线程中设置此属性,因为它可能需要几分钟的时间。在后台线程中设置它可以保持UI的响应,并让我向用户显示加载指示。

//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(); 
} 

如果:

我通过将关断到分派器和开始在后台线程调度器固定内存泄漏(所造成的不运行的背景分派器保持到的CollectionView的引用)后台线程稍后会被重用(例如,因为它是线程池线程,由BackgroundWorker启动),您不能像上面的代码那样使用BeginInvokeShutdown:关闭调度程序不能再次启动。在这种情况下,用这个来代替BeginInvokeShutdown的:

Dispatcher.CurrentDispatcher.BeginInvoke((Action) delegate() { Dispatcher.ExitAllFrames(); }, DispatcherPriority.Background); 

这将确保运行()方法返回,但调度员可以稍后再上启动。

编辑:正如米奇在下面的评论中提到的,当多个线程可以同时执行Run()时,请小心。如有必要,在Run()中添加一个锁。

+1

这是一个非常有趣的案例,了解调度员使用谢谢! – Safe

+0

这将导致随机异常,因为您已经有效地删除了仅由一个调度程序提供的同步。 – Mitch

+0

@Mitch,在第二个调度器上只执行一个动作,它不需要与UI线程同步。 – Coder14

1

我用这个当我需要更新一些控件,并从我的后台任务在我的UI线程绑定:

Application.Current.Dispatcher.Invoke(
    DispatcherPriority.Loaded, 
    new Action(() => { 

     // Code here 

    }) 
); 

如果不是这样,你可以在你想在你的UI做什么更具体的线程

+0

我无法更改调用Dispatcher.CurrentDispatcher的代码。它是WPF框架的一部分。 – Coder14

+0

噢好吧我不明白我的问题 – Safe

0

从后台线程访问当前的调度程序不会给你的UI调度,它会给你一个新的后台线程。

要么从前台线程调用CurrentDispatcher,并将结果传递给后台线程,要么调用DependencyObject.Dispatcher以获取窗口或其他控件的调度程序。


编辑:我刚刚读了更密切的问题。由于您不控制调用CurrentDispatcher的代码,因此它的唯一工作方式是从UI线程调用该代码。