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

独自空忆成欢 提交于 2019-12-10 22:49:53

问题


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)?


回答1:


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




回答2:


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.




回答3:


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().



来源:https://stackoverflow.com/questions/41939468/wpf-framework-using-dispatcher-currentdispatcher-in-background-thread-causing-m

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!