【问题标题】:Task keeping Form instance open?保持表单实例打开的任务?
【发布时间】:2012-03-14 00:56:41
【问题描述】:

我有一个打开另一个窗体的 Windows 窗体。在辅助形式中,它异步启动一个任务。如果用户启动任务然后取消它并快速关闭表单,则表单被 Disposed 并设置为 null 但是当任务从被取消中恢复时,我仍然会收到 MessageBox.Show 发生

public class MyMainForm : Form
{
  public void OpenChildForm()
  {
     MyChildForm form = new MyChildForm();
     form.ShowDialog();
     form.Dispose();
     form = null;
  }
}

public class MyChildForm : Form
{

  private CancellationTokenSource MyTokensource;
  private Task task; 


  public void StartTask()
  {
     MyTokensource = new CancellationTokenSource();
     task = Task.Factory.StartNew(() => MyMethod(MyTokensource.Token), MyTokensource.Token);
  }

  public void MyMethod(CancellationToken token)
  {
      var result = StaticClass.DoSomethingLengthy(token);  //The cancel make take a couple of seconds to return here
      if (result == Cancelled)
      {
         MessageBox.Show("Cancelled");
         UpdateLabel("Cancelled")
       }
  }

  public void ButtonClose_Click()
  { 
    if (task != null && !task.IsCompleted)
    {
      MyTokensource.Cancel();
    }
    this.Close();
  }
}

【问题讨论】:

    标签: c# .net winforms asynchronous task-parallel-library


    【解决方案1】:

    表单已处理并设置为空,但是当任务从被取消中恢复时,我仍然会收到 MessageBox.Show 发生

    将作为对表单的引用的变量设置为 null,甚至在表单上调用 Dispose(),实际上并不会销毁表单。 Task 一直在执行,直到它被取消(CancellationTokenSource 被设计为取消的协作模型)。

    因此,您需要显式处理取消任务时出现的代码路径。这可能就像检查您是否已经被处置一样简单,即:

    if (this.IsDisposed)
        return; // Just break out if we canceled and shut down
    
    // Your other code....
    if (result == Cancelled)
        MessageBox.Show("Cancelled");
    

    【讨论】:

    • 虽然我不明白为什么你说它没有破坏事件,但我调用了 dispose,但它起作用了。我猜是因为 GC 还没有开始!
    • @Jon Dispose 对内存(必然)没有直接影响或任何托管对象。它只是一种方法,(按照惯例)用于释放本机资源。它对 CLR 分配和管理的内存或对象的影响为零。请参阅:reedcopsey.com/series/idisposable 当然,有问题的“资源”可能是内存,但也可能是其他任何东西(或什么都没有)。
    【解决方案2】:

    这是有道理的。 Task 关闭异步执行,它的执行生命周期与Form 的生命周期无关。如果Form 已经被/已被处理,您只需要添加一个明确的检查以确保您不显示MessageBox

    if(result == Cancelled
                &&
       !(this.Disposing
               ||
        this.IsDisposed))
    {
        MessageBox.Show("Cancelled");
    }
    

    【讨论】:

    • 但是为什么像 UpdateLabel 这样的东西没有倒塌,因为我会认为标签不会存在等等
    • 这不应该是&& !(this.Disposing || this.IsDisposed)吗?
    • @ThorstenDittmar 是的,忘了我不是。更新示例。
    【解决方案3】:

    另一件需要注意的事情:确保您没有多次致电StartTask()

    如果是这样,您最终会得到多个异步任务,以及 CancellationTokenSource 的多个实例(其中只有一个仍被表单引用)。

    【讨论】:

      【解决方案4】:

      窗体的实例仍然存在,即使窗口可能不可见。要确保在关闭表单后不显示MessageBox,请将事件添加到OnClosing 并将成员变量m_formClosed 设置为true。仅当成员变量为false 时才显示消息。

      if (result == Cancelled && !m_formClosed)
          MessageBox.Show("Cancelled");
      

      【讨论】:

        【解决方案5】:

        尽管您调用了 Dispose() 并将引用设置为 null,但 GC 可能没有收集到表单。这是正常的,因为 GC 是不确定的。

        由于IDisposable的实现方式,您可以查看表单上的IsDisposedIsDisposing属性,查看Dispose()方法是否已经被调用或正在运行。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-04-27
          • 2016-03-09
          • 1970-01-01
          • 2015-09-30
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多