【发布时间】:2015-08-21 10:13:02
【问题描述】:
在我的应用程序中,我在后台(线程池)线程中创建 Freezable 对象,冻结它们,然后在主线程上显示它们。一切正常,除了一段时间后,整个系统变得迟缓,应用程序最终崩溃。
我已经设法将问题减少到这一行:
var temp = new DrawingGroup();
如果您在不同的后台(非 UI)线程上运行得足够频繁,整个系统就会变得迟缓,最终应用程序崩溃。
(在我的真实应用程序中,然后我在这个对象上绘制一些东西,冻结它,然后在主线程上显示它,但这不是重现问题所必需的。)
重现该问题的完整代码(复制到默认的空白 wpf 应用程序中):
public partial class MainWindow : Window
{
private DispatcherTimer dt;
public MainWindow()
{
InitializeComponent();
dt = new DispatcherTimer();
dt.Interval = TimeSpan.FromSeconds(0.1);
dt.Tick += dt_Tick;
dt.IsEnabled = true;
}
private int counter = 0;
void dt_Tick(object sender, EventArgs e)
{
for (int i = 0; i < 100; i++)
{
var thread = new Thread(MemoryLeakTest);
thread.Start();
}
Title = string.Format("Mem leak test {0}", counter++);
}
private void MemoryLeakTest()
{
try
{
var temp = new DrawingGroup();
temp.Freeze();
}
catch (Exception e)
{
dt.IsEnabled = false;
MessageBox.Show(e.Message+Environment.NewLine+e.StackTrace);
}
}
}
在约 150 次计时器运行后(即在短时间内创建了大约 15000 个线程后),我得到了这个异常:
Not enough storage is available to process this command
bei MS.Win32.HwndWrapper..ctor(Int32 classStyle, Int32 style, Int32 exStyle, Int32 x, Int32 y, Int32 width, Int32 height, String name, IntPtr parent, HwndWrapperHook[] hooks)
bei System.Windows.Threading.Dispatcher..ctor()
bei System.Windows.DependencyObject..ctor()
bei System.Windows.Media.DrawingGroup..ctor()
bei WpfApplication5.MainWindow.MemoryLeakTest() in ...
我认为正在发生的事情是这样的:
-
DrawingGroup派生自DependencyObject,DependencyObject的构造函数使用Dispatcher.CurrentDispatcher,然后为该线程创建一个新的Dispatcher。 - 新的调度程序分配了一些 Win32 资源。
- 在 Reflector 中查看
HwndWrapper的终结代码,我认为HwndWrapper尝试使用Dispatcher.BeginInvoke同步它自己的清理。 - 由于此后台线程从不启动消息循环,因此将永远不会调用清理代码 => 资源泄漏
有没有办法解决或解决这个问题?
到目前为止我所尝试的:
- 显然,使用
ThreadPool或Tasks而不是手动创建线程会延迟此问题。但是ThreadPool也会随着时间的推移创建和关闭新线程,因此只会延迟问题,而不是解决方案。 - 在每个线程结束时强制进行完整的 GC 收集并没有改变任何东西。这与垃圾收集的不确定性无关。
- 在后台线程结束时手动调用
Dispatcher.InvokeShutdown似乎可行,但我不知道如何确保在每个ThreadPool线程结束时调用它。不用写我自己的ThreadPool,那就是……
【问题讨论】:
标签: c# wpf multithreading