WPF 框架在后台线程中使用 Dispatcher.CurrentDispatcher,导致内存泄漏

WPF framework using Dispatcher.CurrentDispatcher in background thread, causing memory leak

我正在使用 WPF CollectionView 并且在后台线程中设置过滤器,因为应用此过滤器需要很长时间。

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

但是,因为这是在后台线程中执行的,所以这个Action永远不会执行(永远不会启动这个线程的Dispatcher),导致内存泄漏:Dispatcher保持对CollectionView的引用。

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

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

当我需要从我的后台任务更新我的 UI 线程上的某些控件和绑定时,我会使用它:

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

        // Code here

    })
);

如果不是这样,您能否在 UI 线程

上更具体地说明您想做什么

从后台线程访问当前调度程序不会为您提供 UI 调度程序,它会为您提供一个新的后台线程调度程序。

从前台线程调用 CurrentDispatcher 并将结果传递给后台线程,或者调用 DependencyObject.Dispatcher 以获得 window 或其他控件的调度程序。


编辑:我只是更仔细地阅读了这个问题。由于您无法控制调用 CurrentDispatcher 的代码,因此唯一可行的方法是从 UI 线程调用该代码。

明确一点:我没有在我的代码中使用 Dispatcher.CurrentDispatcher。这是在 WPF 框架代码中使用的,所以我无法更改它。 这段代码在后台线程中执行,因为我在后台线程中设置过滤器。我在后台线程中设置此 属性,因为它最多可能需要几分钟。在后台线程中设置它可以保持 UI 响应并让我向用户显示加载指示。

我通过向 Dispatcher 添加 Shutdown 并在后台线程中启动调度程序来修复内存泄漏(由 not-running 后台 Dispatcher 保持对 CollectionView 的引用引起):

//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:

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

这将确保 运行() 方法 returns,但稍后可以再次启动调度程序。

编辑:正如 Mitch 在下面的评论中提到的,当多个线程可以同时执行 运行() 时要小心。如有必要,在 运行().

周围添加一个锁