我正在研究一个大型WinForms项目,该项目在同一UI线程上控制多个窗体。
其中一些表格具有从数据库中获取和分析某些数据的能力,这是通过使用await完成的(在等待数据并对其进行分析时不会冻结所有表格)。
[我想确保当UI线程以可处置的形式等待后,UI线程继续运行时,如果我没有问题(如果用户在Task仍在运行时关闭了表单,则为该状态。
我在Google中进行了搜索并找到了它:
How to better handle disposed controls when using async/await
在此页面中,作者写道,在上述情况下(当UI线程尝试以已处置形式访问标签时,会引发异常。
我对此情况进行了测试,但未抛出任何异常:
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_Form窗体。
尽管该代码运行时没有引发任何异常,尽管UI线程试图更改Disposed标签(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";
}
}
我通过单击button2创建Simple_Form。
我尝试通过单击button1或仅单击Simple_Form表单上的“ X”按钮来处理它,两种方法都起作用,没有引发任何异常。
编辑2:根据建议更改了代码,原始问题仍然存在。
[搞笑,这是我的链接问题。无论如何,解决方案很简单。使用此模式:
await Whatever();
if (IsDisposed)
return;
为什么这是必要的?那么await
调用会捕获当前的SynchronizationContext
,然后将其回传给它。
这意味着您回到了原始线程。在这种情况下,GUI线程。
虽然异步发生,但出于各种原因(最常见的是用户关闭表单)可以处置GUI对象。记住,await
是not阻止呼叫。
因此,每次在GUI线程上单击IsDisposed
时,都应使用await
检查来保护自己。
具体来说,请在同一方法中调用await
之后修改的所有控件上检查此标志(包括从Form
派生的Control
。]
但是],您需要了解任务如何处理异常:
如果执行await
,则可以围绕它try ... catch
。如果dont
await
,则异常不会冒泡。这是一个简单的示例。引发异常,您可以捕获除非等待它。如果您不使用Task.Run(() => { ... });
这将不
await
,则可以使用Task.Exception
检查异常,如下所示:var task = Task.Run(() => { ... }); //...SNIP... if (task.Exception != null) //Do something
您的代码的其他问题:
public async void StartCheck(Form1 caller)
应该是
异步方法不应返回public async Task StartCheck(Form1 caller)
<< [only
Task
或Task<T>
的时间是如果不允许使用该签名(如按钮单击处理程序)。最后,请使用Task.Delay
而不是Thread.Sleep
。更改await Task.Run(() =>
{
Thread.Sleep(10000);
});
收件人
await Task.Delay(10000);
尝试一下:编辑
public async Task startCheck(Form1 caller)
{
await Task.Delay(10000);
this.Show();
}
关闭newForm
后但在await
完成之前。将引发异常。
这还会导致您期望的行为:
newForm.Dispose(true);