C# 从等待返回时已释放控件

C# 从等待返回时已释放控件,c#,multithreading,winforms,dispose,C#,Multithreading,Winforms,Dispose,我正在从事一个大型WinForms项目,该项目在同一UI线程上控制多个表单 其中一些表单能够从数据库中获取和分析一些数据,这是通过使用wait完成的(用于在等待数据并分析数据时不冻结所有表单) 我想确保当UI线程在已处理表单中等待后继续运行时(如果用户在任务仍在运行时关闭表单),我没有问题 我在谷歌上搜索了一下,发现: 在本页中,作者写道,在上述情况下会引发异常(当UI线程尝试访问已处置表单中的标签时) 我对这种情况进行了测试运行,没有引发任何异常: public partial cl

我正在从事一个大型WinForms项目,该项目在同一UI线程上控制多个表单

其中一些表单能够从数据库中获取和分析一些数据,这是通过使用wait完成的(用于在等待数据并分析数据时不冻结所有表单)

我想确保当UI线程在已处理表单中等待后继续运行时(如果用户在任务仍在运行时关闭表单),我没有问题

我在谷歌上搜索了一下,发现:

在本页中,作者写道,在上述情况下会引发异常(当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_表单

尽管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
not
Thread.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
not
Thread.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,因此在这种情况下,标签文本更改不会引发异常,在上述情况下,哪种更改之王会引发异常?我不知道是否必须检查此项。每次等待后都会显示此项,以确保在用户关闭时不会引发异常