从等待返回时已经放置的控件

问题描述 投票:0回答:1

我正在研究一个大型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:根据建议更改了代码,原始问题仍然存在。

c# multithreading winforms dispose
1个回答
2
投票

[搞笑,这是我的链接问题。无论如何,解决方案很简单。使用此模式:

await Whatever();
if (IsDisposed)
    return;

为什么这是必要的?那么await调用会捕获当前的SynchronizationContext,然后将其回传给它。

这意味着您回到了原始线程。在这种情况下,GUI线程。

虽然异步发生,但出于各种原因(最常见的是用户关闭表单)可以处置GUI对象。记住,awaitnot阻止呼叫。

因此,每次在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

异步方法不应返回TaskTask<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);

© www.soinside.com 2019 - 2024. All rights reserved.