当ThreadPool WaitCallback在关闭后调用Form时,Form会发生什么?

时间:2012-03-04 17:14:59

标签: c# winforms multithreading

让我们假设在Windows窗体中,我开始这样一个长期运行的任务:

ThreadPool.QueueUserWorkItem(new WaitCallback(UpdateDailyTasksChart));

然后,这个函数会运行一段时间,但我会在它完成之前关闭窗口。

private void UpdateDailyTasksChart (object param)
{
  //Call Data Access Layer, go get MySQL Data. But I close the Form.
  UICallback(chartData); //The Form is closed when this is executed.
}

现在这就是UICallback的作用:

private delegate void UICallbackDel(object chartData);
private void UICallback (object chartData)
{
  if (InvokeRequired)
  {
    this.Invoke(new UICallbackDel(UICallback), chartData);
  }
  else
  {
    aButtonOnMyForm.Visible = false; //But the Form has been closed!
  }
}

奇怪的是,这段代码不会崩溃。

我在Form_Closed事件中放置了断点,执行。我没有检查Form是否仍然存在,例如,通过类变量声明它。但我的猜测确实如此。

所以问题是:GC只会在我的线程结束时收集表单?或者它会发生什么?

3 个答案:

答案 0 :(得分:4)

这里有两点:

  1. 如果您明确处置了表单,则可能会在工作项委托执行时引发ObjectDisposedException
  2. 您的工作项委托通过thisInvokeRequired(反过来引用this)和aButtonOnMyForm来保存对表单的引用。因此,只要ThreadPool线程尚未完成执行,您的表单就不符合垃圾回收的条件。这就是代码不会崩溃的原因。 (注意:this始终可以隐式访问。)
  3. 从中得出的另一件事是,通常明智的做法是在表单关闭之前从表单中注销外部事件处理程序,以免创建内存泄漏。如果你注册一个lambda表达式或匿名委托,你需要保存对它的引用,以便以后取消注册。简单地复制和粘贴相同的代码是行不通的。

答案 1 :(得分:2)

您可能需要查看this帖子。

基本上,您可能会或可能不会获得ObjectDisposedException,具体取决于您尝试在表单上调用时GC正在执行的操作。你可以尝试抓住它,但它无法可靠地测试,因此这比任何事情都更糟糕。

答案 2 :(得分:2)

垃圾收集器不关心对象是“关闭”,“处置”还是类似的东西。它只关心对象是否仍然可访问。在您的情况下,仍然可以使用this(隐式或显式)访问表单。这就是您的应用程序不会崩溃的原因。

当然,如果一个对象被关闭或处置或类似的东西,它就有权抛出ObjectDisposedException或类似的任何你调用它的方法。但它当然不必这样做。