为什么我的线程在显示Windows窗体后立即终止?
我有一个Windows窗体应用程序(Form1),允许用户打开另一个窗体(FormGraph)。 为了打开FormGraph应用程序,我使用一个打开它的线程。
这是线程正在运行的代码:
private void ThreadCreateCurvedGraph() { FormGraph myGraph = new FormGraph(); myGraph.CreateCurvedGraph(...); myGraph.Show(); }
我的问题是myGraph
在打开后myGraph
关闭。
1)有谁知道为什么会这样,以及如何让myGraph
保持开放状态?
2)用户关闭myGraph
,如何终止线程?
非常感谢!
问题不在发布的代码段中。 您需要使用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线程
有关如何使用多个线程(每个表单/表单组一个)运行高性能用户界面的详细概述。
你基本上错过的是调用Application.Run来在单独的UI线程上设置消息泵。
我认为一旦消息泵的最后一种forms关闭 – 它将自行处理并结束。
请注意,所有这些假设你想在一个单独的UI线程中打开窗口…否则你需要调用回主UI线程来创建窗口的所有操作,因此它会附加到现有的消息泵。 两者都有好的情况 – 一个保持简单,另一个允许更多的性能,因为每个窗口都有一个单独的消息泵,因此可以单独行动 – 这例如在交易应用程序中使用很多,可能需要更新图表如果在UI中运行单线程,则有许多屏幕并且有瓶颈。
根据经验,您应该避免从线程操纵UI(创建表单是对UI的一种操作)。 您应该始终从主线程操纵UI。
表单正在关闭,因为该线程已完成,因此随其资源(表单)一起被释放。 要使线程保持运行,您需要一个循环
例如
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。
我将在这里添加与其他用户相同的...你应该有充分的理由不使用主线程。
如果为表单准备数据需要一段时间,则可以在单独的线程中执行此操作以使应用程序保持响应。 数据准备好后,您可以将对象返回到主线程,让它显示它。
您应该为表单中的对象声明一个变量而不是方法中的本地变量,以便在退出线程时它仍然存在。
当您准备好显示表单时,可以使用Invoke方法进行将在主线程中执行的方法调用。
不要在非主线程中创建和显示表单。 在主表单线程中执行。
或者这样做:
private void ThreadCreateCurvedGraph() { FormGraph myGraph = new FormGraph(); myGraph.CreateCurvedGraph(...); Application.Run(myGraph); }
但第一版更好
为什么要在新线程上创建表单? 有时您需要使用新线程,但有时您可以在主线程上使用form.ShowDialog()。
如果您将表单显示为对话框怎么样? 您可以使用
private void ThreadCreateCurvedGraph() { FormGraph myGraph = new FormGraph(); myGraph.CreateCurvedGraph(...); myGraph.ShowDialog(); }
这样调用将阻塞,直到myGraph表单关闭。 正如你在一个独立的线程上创建myGraph一样,调用阻塞的ShowDialog应该只阻止该线程。
也许这是垃圾收集:
ThreadCreateCurvedGraph()
退出后, myGraph
超出范围并关闭。
您需要组织线程的方式来保持实例并等待(使用阻塞等待)它关闭。
编辑:例如添加:
Application.Run(myGraph)
到方法的最后。
(见TomTom的评论)