任务保持Form实例打开?

时间:2012-03-13 15:48:51

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

我有一个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();
  }
}

5 个答案:

答案 0 :(得分:2)

这是有道理的。 Task异步执行,其执行生命周期与Form的生命周期无关。您只需添加一个明确的检查,以确保在MessageBox已被处置/已被处置时,您不会显示Form

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

答案 1 :(得分:2)

  

表单是Disposed并设置为null但是当任务从取消回来时我仍然得到MessageBox.Show发生

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

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

if (this.IsDisposed)
    return; // Just break out if we canceled and shut down

// Your other code....
if (result == Cancelled)
    MessageBox.Show("Cancelled");

答案 2 :(得分:1)

需要注意的另一件事是:确保您不会多次拨打StartTask()

如果是这样,您最终会遇到多个异步任务,并且有多个CancellationTokenSource实例(其中只有一个仍由表单引用)。

答案 3 :(得分:0)

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

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

答案 4 :(得分:0)

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

由于IDisposable的实施方式,您可以检查表单上的IsDisposedIsDisposing属性,看看是否已调用Dispose()方法或是在运行过程中。