C# 从等待返回时已释放控件
我正在从事一个大型WinForms项目,该项目在同一UI线程上控制多个表单 其中一些表单能够从数据库中获取和分析一些数据,这是通过使用wait完成的(用于在等待数据并分析数据时不冻结所有表单) 我想确保当UI线程在已处理表单中等待后继续运行时(如果用户在任务仍在运行时关闭表单),我没有问题 我在谷歌上搜索了一下,发现: 在本页中,作者写道,在上述情况下会引发异常(当UI线程尝试访问已处置表单中的标签时) 我对这种情况进行了测试运行,没有引发任何异常:C# 从等待返回时已释放控件,c#,multithreading,winforms,dispose,C#,Multithreading,Winforms,Dispose,我正在从事一个大型WinForms项目,该项目在同一UI线程上控制多个表单 其中一些表单能够从数据库中获取和分析一些数据,这是通过使用wait完成的(用于在等待数据并分析数据时不冻结所有表单) 我想确保当UI线程在已处理表单中等待后继续运行时(如果用户在任务仍在运行时关闭表单),我没有问题 我在谷歌上搜索了一下,发现: 在本页中,作者写道,在上述情况下会引发异常(当UI线程尝试访问已处置表单中的标签时) 我对这种情况进行了测试运行,没有引发任何异常: public partial cl
public partial class Simple_Form : Form
{
public Simple_Form()
{
InitializeComponent();
}
public async Task startCheck(Form1 caller)
{
caller.richTextBox1.Text += "Thread:" + Thread.CurrentThread.ManagedThreadId.ToString() + "|start\n";
label1.Text = "Thread:" + Thread.CurrentThread.ManagedThreadId.ToString() + "|start";
await Task.Delay(10000);
caller.richTextBox1.Text += "Thread:" + Thread.CurrentThread.ManagedThreadId.ToString() + "|stop\n";
caller.richTextBox1.Text += "Thread:" + Thread.CurrentThread.ManagedThreadId.ToString() + "|" + label1.IsDisposed + "\n";
label1.Text = "Thread:" + Thread.CurrentThread.ManagedThreadId.ToString() + "|stop";
}
我试图在UI线程处于等待状态时运行StartCheck并关闭Simple_表单
尽管UI线程试图更改已释放的标签(label1),但此代码运行时未引发任何异常,label1.IsDisposed为“true”
自从创建上面的页面以来,我是否遗漏了一些内容,或者此功能是否发生了更改
编辑:
根据要求,我运行的主要表单:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
Simple_Form newForm;
private async void button2_Click(object sender, EventArgs e)
{
newForm = new Simple_Form();
newForm.Show();
await newForm.startCheck(this);
return;
}
private void button1_Click(object sender, EventArgs e)
{
newForm.Dispose();
}
private void button3_Click(object sender, EventArgs e)
{
richTextBox1.Text += "Thread:" + Thread.CurrentThread.ManagedThreadId.ToString() + "|Still alive.\n";
}
}
我通过点击按钮2创建简单表单
我试着通过点击按钮1或者只点击简单表单上的“X”按钮来处理它,这两种方式都有效,没有任何异常抛出
编辑2:按照建议更改代码,原始问题仍然有效。有趣的是,这是我的链接问题。无论如何,解决办法很简单。使用此模式:
await Whatever();
if (IsDisposed)
return;
为什么这是必要的?嗯,wait
调用捕获当前的SynchronizationContext
,然后发回给它
这意味着你回到了最初的思路。在本例中,GUI线程
当这种情况异步发生时,GUI对象可以出于各种原因(通常是用户关闭的表单)进行处理。记住,await
不是一个阻塞调用
因此,每次在GUI线程上执行等待时,您都应该使用IsDisposed
检查来保护自己
具体地说,在使用相同方法调用wait
后修改的任何控件上选中此标志(包括从Control
派生的Form
)
但是您需要了解任务如何处理异常:
如果您执行等待
操作,您可以尝试。。。抓住它周围的
。如果不使用等待
异常,请不要冒泡。这里有一个简单的例子
Task.Run(() => { ... });
除非等待,否则这不会引发您可以捕获的异常。如果您没有使用wait
,您可以使用任务检查异常。异常
如下:
var task = Task.Run(() => { ... });
//...SNIP...
if (task.Exception != null)
//Do something
代码的其他问题:
public async void StartCheck(Form1 caller)
应该是
public async Task StartCheck(Form1 caller)
异步方法不应返回Task
或Task
的唯一时间是不允许使用该签名(如按钮单击处理程序)
最后,使用Task.Delay
notThread.Sleep
。改变
await Task.Run(() =>
{
Thread.Sleep(10000);
});
到
编辑
试试这个:
public async Task startCheck(Form1 caller)
{
await Task.Delay(10000);
this.Show();
}
关闭newForm
后,但在wait
完成之前。将引发异常
这也会导致您预期的行为:
newForm.Dispose(true);
有趣的是,这是我的关联问题。无论如何,解决办法很简单。使用此模式:
await Whatever();
if (IsDisposed)
return;
为什么这是必要的?嗯,wait
调用捕获当前的SynchronizationContext
,然后发回给它
这意味着你回到了最初的思路。在本例中,GUI线程
当这种情况异步发生时,GUI对象可以出于各种原因(通常是用户关闭的表单)进行处理。记住,await
不是一个阻塞调用
因此,每次在GUI线程上执行等待时,您都应该使用IsDisposed
检查来保护自己
具体地说,在使用相同方法调用wait
后修改的任何控件上选中此标志(包括从Control
派生的Form
)
但是您需要了解任务如何处理异常:
如果您执行等待
操作,您可以尝试。。。抓住它周围的
。如果不使用等待
异常,请不要冒泡。这里有一个简单的例子
Task.Run(() => { ... });
除非等待,否则这不会引发您可以捕获的异常。如果您没有使用wait
,您可以使用任务检查异常。异常
如下:
var task = Task.Run(() => { ... });
//...SNIP...
if (task.Exception != null)
//Do something
代码的其他问题:
public async void StartCheck(Form1 caller)
应该是
public async Task StartCheck(Form1 caller)
异步方法不应返回Task
或Task
的唯一时间是不允许使用该签名(如按钮单击处理程序)
最后,使用Task.Delay
notThread.Sleep
。改变
await Task.Run(() =>
{
Thread.Sleep(10000);
});
到
编辑
试试这个:
public async Task startCheck(Form1 caller)
{
await Task.Delay(10000);
this.Show();
}
关闭newForm
后,但在wait
完成之前。将引发异常
这也会导致您预期的行为:
newForm.Dispose(true);
您如何调用StartCheck
?你能告诉我你在哪里调用表单吗?没问题,我编辑了原来的帖子。你应该得到一个警告:不要等待newForm.startCheck(这个)
另外,startCheck
方法应该返回一个任务
@Fabio I,该任务刚刚通过一个简单的(同步的POC)验证过。处置的标签允许对其文本属性进行赋值。@Oguz Ozgul,因此在这种情况下,标签文本更改不会引发异常,在上述情况下,哪种更改之王会引发异常?我不知道是否必须检查此项。每次等待后都会显示此项,以确保在用户关闭时不会引发异常