【问题标题】:Why Does my Thread Terminate Immediately After Showing a Windows Form?为什么我的线程在显示 Windows 窗体后立即终止?
【发布时间】:2010-03-18 12:02:53
【问题描述】:

我有一个 Windows 窗体应用程序 (Form1),它允许用户打开另一个窗体 (FormGraph)。为了打开 FormGraph 应用程序,我使用了一个线程来打开它。
这是线程正在运行的代码:

private void ThreadCreateCurvedGraph()
{
    FormGraph myGraph = new FormGraph();
    myGraph.CreateCurvedGraph(...);
    myGraph.Show();
}

我的问题是myGraph 在打开后立即关闭。
1) 有谁知道为什么会这样以及如何让myGraph 保持开放?
2)用户关闭myGraph后,如何终止线程?
非常感谢!

【问题讨论】:

    标签: c# winforms multithreading


    【解决方案1】:

    问题不在于贴出的sn-p。您需要使用 Application.Run() 或 Form.ShowDialog() 开始一个新的消息循环。您还需要注意线程属性,因此它适合充当 UI 线程。例如:

      Thread t = new Thread(() => {
        Application.Run(new Form2());
        // OR:
        //new Form2().ShowDialog();
      });
      t.SetApartmentState(ApartmentState.STA);
      t.IsBackground = true;
      t.Start();
    

    这里有一些尴尬的选择。主线程上的任何表单都不能拥有该表单,这通常会导致 Z 顺序问题。当 UI 线程的主窗体关闭时,您还需要做一些有意义的事情。通过使用 IsBackground 在这里草率地解决了。

    Windows 旨在支持在一个线程上运行多个窗口。仅当您真的必须时才使用这样的代码。您永远不必...

    【讨论】:

    • 很好,除了最后一部分——你永远不必这样做。曾经见过一个交易应用程序......做了这么多更新,UI 落后于 6 个屏幕;)有需要。
    • @TomTom:我不知道为什么按照我说的在这里放一个 ShowDialog 没有错。
    【解决方案2】:

    您遇到的主要问题是您没有在新线程中建立消息泵。

    检查

    Run multiple UI Threads

    了解如何使用多个线程(每个表单/表单组一个)运行高性能用户界面。

    您基本上错过的是调用 Application.Run 以在单独的 UI 线程上设置消息泵。

    我认为一旦消息泵的最后一种形式关闭 - 它会自行处理并结束。

    请注意,所有这些都假定您希望在单独的 UI 线程中打开窗口...否则您需要调用回主 UI 线程来创建和所有操作窗口,因此它会附加到现有的消息泵。两者都有很好的案例 - 一个保持简单,另一个允许更多性能,因为每个窗口都有一个单独的消息泵,因此可以单独操作 - 例如,这在可能需要更新图表的交易应用程序中使用很多如果在 UI 中运行单线程,则会出现许多屏幕并且存在瓶颈。

    【讨论】:

      【解决方案3】:

      根据经验,您应该避免从线程操作 UI(创建表单是对 UI 的一种操作)。您应该始终从主线程操作 UI。

      【讨论】:

      • 如果您认为表单只是 UI 的一部分,而不是 UI 的容器,那么它就是。
      • 还是错了。多 UI 线程有很好的案例。
      • @TomTom:答案从 “根据经验 [...]” 开始。这不排除有很大的案例,只是它不应该是首选。通常,该问题缺少发布者为什么希望在线程上创建表单的基本信息;它可能是也可能不是一个好主意。
      【解决方案4】:

      表单正在关闭,因为线程已完成,因此连同其资源(表单)一起被释放。要使线程保持运行,您需要一个循环

      例如

      private void ThreadCreateCurvedGraph()
      {
          FormGraph myGraph = new FormGraph();
          myGraph.CreateCurvedGraph(...);
          myGraph.Show();
          while (myGraph.IsOpen)
          {
               //process incoming messages <- this could be fun on a thread....
          }
      }
      

      您需要一种设置 IsOpen 的方法(如超时或按钮),显然您需要实际创建 IsOpen 作为表单的属性,并在创建表单时将其设置为 true。

      我会和其他用户一样在这里添加...你应该有充分的理由不使用主线程。

      【讨论】:

      • 这里有很多人并不真正了解多个消息泵的概念——这很痛苦。 RTFM,伙计们。做一个多线程 UI 确实有很好的理由 - 我有一个为 6 个窗口使用多达 6 个 UI 线程。都在忙着重新粉刷。这是完全支持的。
      【解决方案5】:

      如果为表单准备数据需要一段时间,您可以在单独的线程中执行此操作以保持应用程序响应。数据准备好后,您可以将对象返回到主线程并让它显示出来。

      你应该在表单中为对象声明一个变量,而不是在方法中本地声明,这样当你退出线程时它仍然存在。

      当您准备好显示表单时,您可以使用 Invoke 方法进行方法调用,该方法调用将在主线程中执行。

      【讨论】:

      • 不是用户要求的。他非常清楚想要在单独的 UI 线程中打开另一个窗口,这 - 顺便说一下 - 有时确实有很好的理由(交易应用程序为此而闻名)。
      • @TomTom:请不要试图对 OP 真正想要什么做出裁决。很明显,当前使用了一个线程,但不清楚它实际上是否是所需的解决方案。仅仅因为 OP 要求某些东西并不一定意味着它是正确的解决方案。如果一个人只回答所问的问题,那么除了“是”或“否”之外,很多问题都无法回答,因为实际提出的问题并不是提出问题的意思。
      • 请不要尝试对 OP 真正想要的做出裁决。阅读他的帖子。以他为榜样,推断他试图做什么。不要以为程序员是个漫不经心的白痴。
      • @TomTom:询问所提出问题案例的根本原因与假设发布者是白痴不同。它只是有助于提供更好的答案。
      • @TomTom:我提出了一个解决方案,但这并不意味着我要对任何事情做出裁决。我已经阅读了他的帖子,并以他为榜样并推断了他试图做的事情。仅仅因为我没有得出与您相同的结论并不意味着我错了,也不意味着我认为OP是个白痴。
      【解决方案6】:

      不要在非主线程中创建和显示表单。在主窗体线程中执行。

      或者这样做:

      private void ThreadCreateCurvedGraph()
      {
          FormGraph myGraph = new FormGraph();
          myGraph.CreateCurvedGraph(...);
          Application.Run(myGraph);
      }
      

      但第一个版本更好

      【讨论】:

      • 阅读他的代码。问题:没有建立消息泵。这是拥有多个 UI 线程的充分理由。曾经见过单线程交易应用程序 - 他们很烂(提示:NinjaTrader - 很烂)。
      • 在单线程中进行 UI 处理,并在衍生中进行数据处理。遵循这种模式,单线程、多表单应用程序会很好地工作。我的建议是正确且有效的。
      • 并非总是如此。严重地。看看高端交易应用程序 - 和低端“单线程工作”废话。尝试使用它们。好吧,你不能 - 可能从未交易过。
      • 是的,我从未交易过。但是我参与了丰富ui的投资应用程序的开发,并且这种模式奏效了。
      【解决方案7】:

      为什么要在新线程上创建表单?有时您需要使用新线程,但有时您可以在主线程上使用 form.ShowDialog()。

      【讨论】:

        【解决方案8】:

        如果您将表单显示为对话框会怎样?你可以使用

        private void ThreadCreateCurvedGraph()
        {
             FormGraph myGraph = new FormGraph();
             myGraph.CreateCurvedGraph(...);
             myGraph.ShowDialog();
        }
        

        这样调用将阻塞,直到 myGraph 表单关闭。由于您在调用阻塞 ShowDialog 的单独线程上创建了 myGraph,因此应该只阻塞该线程。

        【讨论】:

        • 好主意!我想我会像你说的那样做。
        • 据我所知它有效,所以我不知道你为什么要给我投反对票。
        • 它被称为“不需要的副作用”。就像一个用户问如何关闭计算机,你告诉他用锤子敲击它——这也会关闭它,但它有副作用。 ShowDialog 也有这些——如果用户想在单独的线程中启动一个 UI,这个 UI 消息泵不应该仅仅因为他得到了一个不好的建议而成为 ni Dialog 模式。
        • 它有什么副作用?我没有注意到任何东西,我已经用这种方法等待对话很长时间了。
        【解决方案9】:

        也许这是垃圾回收:

        ThreadCreateCurvedGraph() 退出后,myGraph 超出范围并关闭。

        您需要组织一种线程方式来保持实例并等待(使用阻塞等待)以使其关闭。

        编辑: 例如添加:

        Application.Run(myGraph)
        

        到方法结束。
        (参见 TomTom 的 cmets)

        【讨论】:

        • 不行,用户需要建立一个MESSAGE PUMP来处理新UI线程的windows消息。
        • @TomTom:那是:Application.Run(myGraph) 会保留实例并等待它关闭吗?
        • 没错。它将建立一个消息泵并等到它的最后一个窗口关闭。这类似于在主线程中发生的事情,它在开始时也不是一个 UI 连接线程(这就是你在那里阅读 Application.Run 的原因)。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-07-13
        • 2023-01-12
        • 2023-01-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多