【问题标题】:How to open a form in a thread and force it to stay open如何在线程中打开表单并强制它保持打开状态
【发布时间】:2008-10-03 15:07:27
【问题描述】:

我仍然无法弄清楚如何在我讨论过的单独 UI 线程中创建 winforms here

为了解决这个问题,我编写了以下简单的测试程序。我只是希望它在名为“UI 线程”的单独线程上打开一个表单,并在表单打开时保持线程运行,同时允许用户与表单交互(旋转是作弊)。我理解为什么以下失败并且线程立即关闭,但不确定我应该如何修复它。

using System;
using System.Windows.Forms;
using System.Threading;

namespace UIThreadMarshalling {
    static class Program {
        [STAThread]
        static void Main() {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            var tt = new ThreadTest();
            ThreadStart ts = new ThreadStart(tt.StartUiThread);
            Thread t = new Thread(ts);
            t.Name = "UI Thread";
            t.Start();
            Thread.Sleep(new TimeSpan(0, 0, 10));
        }

    }

    public class ThreadTest {
        Form _form;
        public ThreadTest() {
        }

        public void StartUiThread() {
            _form = new Form1();
            _form.Show();
        }
    }
}

【问题讨论】:

  • 是的,这只是微软再次未能正确考虑除了单个对话框和单个线程之外的任何内容。正如下面的解决方案所说,您可以启动一个单独的消息泵,但没有方便的方法从 GUI 线程启动它,因此无法使第二个表单作为非模态对话框执行。
  • 公平地说,这个问题是 4 年前的,关于一个 12 岁的技术:)
  • 是的,我知道,但撇开 12 年前人们就想要这个的事实不谈,我敢打赌,MS 的任何修订/新 API 的情况都没有改变.
  • 我认为这不公平,我从来没有深入研究过 WPF,但我所做的似乎并没有受到这些问题的影响(在很多方面 WPF 是一种更成熟的 UI 技术比 HTML)。我认为基于 javascript 的新 UI 可以在节点样式的事件循环上工作,所以我认为 ui 线程甚至不是问题。尽管有许多缺点,但微软的开发工具的发展方式确实值得称赞。

标签: .net winforms multithreading


【解决方案1】:

在新线程上,调用 Application.Run 传递表单对象,这将使线程在窗口打开时运行自己的消息循环。

然后您可以在该线程上调用 .Join 以使您的主线程等到 UI 线程终止,或者使用类似的技巧来等待该线程完成。

例子:

public void StartUiThread()
{
    using (Form1 _form = new Form1())
    {
        Application.Run(_form);
    }
}

【讨论】:

  • 啊...我认为 Application.Run 可能是我丢失的拼图块
【解决方案2】:

我认为您的问题在于这个想法:“在名为 'UI 线程' 的单独线程上打开一个表单”

windows 的工作方式是这样的(请注意 Vista 可能会改变其中的一些现实):

有一个重要的线程称为“主线程”或“UI 线程”。该线程是处理windows messages 的线程,例如“嘿,鼠标点击了这个像素。”

这些消息进入队列,主线程在不忙于做其他事情时处理它们

所以如果你在主线程上调用 foo() 函数,如果它需要很长时间,那么在这段时间内不会处理任何 windows 消息,因此不会发生用户交互。

主线程还在屏幕上绘制 UI,因此长时间运行的 foo() 也会阻止您的应用绘制。

除了这个神圣而特殊的主线程之外的所有其他线程都是 grunt 工作线程。这些工作线程可以做事,但它们永远不能直接与用户界面交互。

这种现实导致了两个问题:

  1. 摆脱主线程:由于您不希望长时间运行的 foo() 停止所有用户交互,因此您需要将该工作交付给工作线程。

  2. 回到主线程:当长时间运行的 foo() 完成时,您可能希望通过在 UI 中执行某些操作来通知用户,但您无法在工作线程中执行此操作,因此您需要“回到”主线程。

所以我认为您在上述程序中的问题非常普遍:您的目标不正确,因为除了神圣的主线程之外,不应在任何线程中调用 _form.Show()。

【讨论】:

    【解决方案3】:
    private void button1_Click(object sender, EventArgs e)
    {
        var t = new Thread(RunNewForm);
        t.Start();
    }
    public static void RunNewForm()
    {
         Application.Run(new Form2());
    }
    

    【讨论】:

    • 虽然您可能不想为每个按钮单击生成一个线程,但此解决方案对我有用。
    【解决方案4】:

    您不能在任何线程中打开 GUI 表单,因为它将缺少消息泵。 您必须通过在线程方法中调用 Application.Run() 来显式启动该线程中的消息泵。如果您需要执行其他操作,另一种选择是在循环中调用 DoEvents(),因为在 Application.Run() 之后,该线程将等待用户在该执行点关闭表单。

    【讨论】:

      【解决方案5】:

      我认为只调用 ShowDialog 而不是 Show 会有所帮助。问题似乎是线程在调用 Show 之后就完成了,之后 Form get 的垃圾被收集了。 ShowDialog 将停止线程,但仍会在其上运行表单事件,因此线程将继续运行,直到表单关闭。

      通常我会反其道而行之。当您想要启动长时间运行的后台任务时,在启动线程上运行表单并启动后台线程。

      我还阅读了您的其他问题,但无法弄清楚您要做什么。 MVP 架构不需要您在不同的线程上运行业务逻辑。多线程很难做到正确,所以如果我真的需要多线程,我只会使用它们。

      【讨论】:

        【解决方案6】:

        您可以锁定线程,直到窗体在方法中停止只是锁定另一个线程。例如:

        public void StartUiThread() {
                _form = new Form1();
                _form.ShowDialog(); //Change Show() to ShowDialog() to wait in thread
            }
        

        这将导致新线程等待对话框关闭。我不知道这是否能解决你的问题,但它解决了我的问题。

        【讨论】:

          猜你喜欢
          • 2013-01-27
          • 1970-01-01
          • 1970-01-01
          • 2015-09-21
          • 1970-01-01
          • 1970-01-01
          • 2013-11-14
          • 1970-01-01
          • 2019-06-06
          相关资源
          最近更新 更多