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

C# 我必须调用Application.ExitThread()吗?,c#,.net,winforms,dialog,mono,C#,.net,Winforms,Dialog,Mono,上面的代码显示了一个文件对话框。一旦我选择了一个文件并按了open,for循环就会执行,但(冻结)对话框仍然存在 取消注释Application.ExitThread()后,对话框将按预期消失 这是否如预期的那样有效?为什么使用不能使窗口消失?在哪里可以找到有关此的详细信息?不,您不必调用应用程序.ExitThread() Application.ExitThread()终止调用线程的消息循环并强制销毁冻结的对话框。虽然“这很有效”,但如果知道冻结的原因,最好解冻对话框 在这种情况下,按open

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

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


这是否如预期的那样有效?为什么使用
不能使窗口消失?在哪里可以找到有关此的详细信息?

不,您不必调用
应用程序.ExitThread()

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


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

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

您的
DoEvents()
调用实质上是“暂停”您的代码,并为其他操作(如UI)提供运行的机会,然后继续。问题是您的UI现在再次冻结,直到再次调用
DoEvents()
。实际上,
DoEvents()
是一个()。你真的不应该用它

你有更好的选择。

将长时间运行的操作放在另一个线程中有助于确保UI保持响应,并尽可能高效地完成工作。处理器能够在两个线程之间来回切换,以产生同时执行的幻觉,而不需要复杂的多进程

实现这一点的一个更简单的方法是使用a,尽管它们通常不受欢迎(出于我不打算在本文中讨论的原因:)。然而,它们仍然是.NET的一部分,并且比其他方法的学习曲线要低,所以我仍然建议新开发人员在业余项目中与它们一起玩

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

JDB已经在《代码不能按预期工作的原因》(一般来说)中解释过。让我补充一点建议解决方法(针对您的具体情况,以及您只需要使用系统对话框,然后像使用控制台应用程序一样继续操作的情况)

要使用
Application.DoEvents()
,确定它似乎可以工作,并且在您的情况下,您没有可重入代码。但是,您确定所有相关消息都已正确处理吗?您应该调用多少次Application.DoEvents()
?您确定您正确地初始化了所有内容(我说的是
ApplicationContext
)?第二个问题更实际,
OpenFileDialog
需要COM,COM(此处)需要
STAThread
STAThread
需要一个消息泵。我不能告诉你它将以何种方式失败,但可以肯定它可能会失败

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

如果使用设计器在窗体内添加对话框,则无需显式调用
dlg.Dispose()
(让WinForms管理对象生存期)

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 (; ;)
            ;
    }
}

我已经测试了您的代码,(文件打开)对话框按预期正确关闭。如果您在处理该对话框后调用它,该对话框是否会消失?没有人会从该片段中重新处理您的问题。在GUI应用程序中,决不能像(;);那样挂起主线程并引发代码冲突,这一点至关重要;很多事情都停止了,尤其是绘画不再发生。因此,窗口中的任何像素都无法更新,您可能会看到窗口顶部显示的任何东西的重影。把时间花在一本体面的教程或书上,如何正确地编写GUI代码是很少被发现的。您的代码不需要调用DoEvents()。您应该在问题中提到这一点。无论如何,这并不能改变问题。需要有一个消息循环。只要对话框在屏幕上,它就会通过
ShowDialog
方法在内部运行一个对话框。但一旦用户撤销了它,消息循环就会消失。没有人再泵送消息,违反STA线程的条件。事实上,您的应用程序已经变得愚蠢,因为它被锁定在一个紧密、无限的循环中。
DoEvents
在大多数情况下都是错误的建议。关于为什么睡眠(或正在讨论中的忙碌等待)会冻结UI(即)以及如何使用计时器、后台工作人员或
async
code正确处理此类情况,已有大量讨论。小心,你正在从煎锅跳进火中。您已将
ExitThread
(一个坏主意)替换为调用
DoEvents
(可以说是一个更坏的主意)。这两个都是非常可怕的黑客,证据表明
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;
    }
}