我有一个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();
}
}
答案 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
的实施方式,您可以检查表单上的IsDisposed
和IsDisposing
属性,看看是否已调用Dispose()
方法或是在运行过程中。