C# 保持窗体实例打开的任务?
我有一个Windows窗体,可以打开另一个窗体。在第二种形式中,它异步启动任务。如果用户启动任务,然后取消任务并快速关闭表单,则表单将被释放并设置为null。但是,当任务取消后返回时,我仍然会收到一个MessageBox。显示发生的情况C# 保持窗体实例打开的任务?,c#,.net,winforms,asynchronous,task-parallel-library,C#,.net,Winforms,Asynchronous,Task Parallel Library,我有一个Windows窗体,可以打开另一个窗体。在第二种形式中,它异步启动任务。如果用户启动任务,然后取消任务并快速关闭表单,则表单将被释放并设置为null。但是,当任务取消后返回时,我仍然会收到一个MessageBox。显示发生的情况 public class MyMainForm : Form { public void OpenChildForm() { MyChildForm form = new MyChildForm(); form.ShowDialog(
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();
}
}
这是有道理的。
任务
已停止异步执行,其执行生命周期与表单
的生命周期无关。如果表单
已被处理,您只需添加一个明确的检查,以确保不显示消息框
:
if(result == Cancelled
&&
!(this.Disposing
||
this.IsDisposed))
{
MessageBox.Show("Cancelled");
}
表单的实例仍然存在,即使窗口可能不可见。要确保关闭表单后不显示
消息框
,请将事件添加到OnClosing
,并将成员变量m_formClosed
设置为true
。仅当成员变量为false
时显示消息
if (result == Cancelled && !m_formClosed)
MessageBox.Show("Cancelled");
表单被释放并设置为null,但是当任务取消后返回时,我仍然会收到一个MessageBox。Show cover
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();
}
}
将引用表单的变量设置为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");
尽管您调用了
Dispose()
并将引用设置为null,但GC可能尚未收集表单。这是正常的,因为GC是不确定的
由于
IDisposable
的实现方式,您可以检查表单上的IsDisposed
和IsDisposing
属性,查看Dispose()
方法是否已被调用或正在运行。另一件需要注意的事情:确保您没有调用StartTask()
不止一次
如果是这样的话,您将得到多个异步任务,以及多个
CancellationTokenSource
实例(其中只有一个实例仍然由表单引用)。但是为什么像UpdateLabel这样的东西不会掉下来呢,因为我本以为标签不存在,等等,这不应该是&!(this.Disposing | | | this.IsDisposed)
?@ThorstenDittmar是的,忘了我的。更新示例。虽然我不明白为什么你说它没有被销毁,但我还是调用了dispose。我猜是因为GC还没有开始@Jon Dispose对内存(必然)或任何托管对象没有直接影响。它只不过是一种方法,(按照惯例)用于释放本机资源。它对CLR分配和管理的内存或对象没有任何影响。请参阅:诚然,所讨论的“资源”可能是内存,但也可能是其他任何东西(或什么都没有)。