我必须调用Application.ExitThread()吗?

时间:2016-06-06 10:55:01

标签: c# .net winforms dialog mono

using System.Windows.Forms;

public class App
{
    [STAThread]
    public static void Main()
    {
        string fname;
        using (var d = new OpenFileDialog())
        {
            if (d.ShowDialog() != DialogResult.OK)
            {
                return;
            }
            fname = d.FileName;
        }
        //Application.ExitThread();
        for (; ;)
            ;
    }
}

上面的代码显示了一个文件对话框。选择文件并按open后,将执行for循环,但(冻结)对话框仍然存在。

取消注释Application.ExitThread()后,对话框会按预期消失。

这是否按预期工作?为什么using没有让窗口消失?我在哪里可以找到更多关于此的信息?

3 个答案:

答案 0 :(得分:12)

您已发现单线程应用程序的主要问题...长时间运行的操作会冻结用户界面。

您的DoEvents()电话主要是"暂停"您的代码并提供其他操作,如UI,运行的机会,然后恢复。问题是您的UI现在再次被冻结,直到您再次呼叫DoEvents()。实际上,DoEvents()very problematic approachsome call it evil)。你真的不应该使用它。

您有更好的选择。

将长时间运行的操作放在另一个线程中有助于确保UI保持响应,并确保您的工作尽可能高效地完成。处理器能够在两个线程之间来回切换,以提供同时执行的假象,而不会出现全面的多进程。

实现这一目标的一个更简单的方法是使用BackgroundWorker,虽然它们通常不受欢迎(原因我不会在这篇文章中介绍:{{3} })。然而,它们仍然是.NET的一部分,与其他方法相比,学习曲线较低,因此我仍然建议新开发人员在业余爱好项目中使用它们。

目前最好的方法是.NET的further reading库。如果您的长时间运行操作已经在一个线程中(例如,它是一个数据库查询而您只是等待它完成),并且如果库支持它,那么您可以使用{{{}来利用任务3}}关键字,而不必三思而后行。即使它不在线程或受支持的库中,您仍然可以启动一个新任务,并通过Tasks在单独的线程中执行它。 .NET任务具有语言支持和更多功能,例如协调多个任务和将任务链接在一起。

答案 1 :(得分:5)

JDB已在his answer 中解释了为什么(一般来说)您的代码无法按预期工作。让我添加一点建议解决方法(针对您的具体情况以及何时只需要使用系统对话框,然后继续进行,就像它是控制台应用程序一样)。

You're trying使用Application.DoEvents(),确定似乎正常工作,在您的情况下,您没有可重入的代码。但是,确定是否正确处理了所有相关消息?你应该多少次拨打Application.DoEvents()?您是否确定正确初始化所有内容(我在谈论ApplicationContext)?第二个问题更实用,OpenFileDialog需要COM,COM(此处)需要STAThreadSTAThread需要消息泵。我不能告诉你它会以哪种方式失败,但肯定可能失败。

首先请注意,应用程序通常使用Application.Run()启动主消息循环。您不希望看到new MyWindow().ShowDialog(),对吗?您的示例没有什么不同,让Application.Run(Form)重载为您创建ApplicationContext(并在表单关闭时处理HandleDestroyed事件,最终会调用 - surprise - Application.ExitThread())。很遗憾OpenFileDialog不会从Form继承,因此您必须在虚拟表单中托管它才能使用Application.Run()

如果您使用设计器在表单中添加对话框,则无需显式调用dlg.Dispose()(让WinForms管理对象生命周期)。

using System;
using System.Windows.Forms;

public class App
{
    [STAThread]
    public static void Main()
    {
        string fname = AskForFile();
        if (fname == null)
            return;

        LongRunningProcess(fname);
    }

    private static string AskForFile()
    {
        string fileName = null;

        var form = new Form() { Visible = false };
        form.Load += (o, e) => { 
            using (var dlg = new OpenFileDialog())
            {
                if (dlg.ShowDialog() == DialogResult.OK)
                    fileName = dlg.FileName;
            }

            ((Form)o).Close();
        };

        Application.Run(form);

        return fileName;
    }
}

答案 2 :(得分:0)

不,您不必致电Application.ExitThread()

Application.ExitThread()终止调用线程的消息循环并强制销毁冻结的对话框。虽然"有效,但如果已知冻结的原因,最好解冻对话框。

在这种情况下,按open似乎会触发一个没有任何机会完成的近距离事件。 Application.DoEvents()给了它机会并使对话框消失。