以两种形式作为不同的线程运行(或者至少有两种形式“并行”运行)
本文关键字:运行 两种 或者 并行 线程 | 更新日期: 2023-09-27 18:25:28
我想做以下(c#)
- 我有一张表格,叫Form1吧
- Form1创建Form2
- Form2的构造函数被执行(GUI,变量..)。Form2的最后一条指令是:创建和显示Form3
- Form2包含Label1
- Form2创建Form3并将其自身的引用传递给Form3
- Form3多次编辑(Form2的)标签1(假设为100)
这没关系…一切都正常,但。。问题是:Form3运行所有操作需要很长时间(假设为10秒)。最后(同时)Form2和Form3都显示了,但我希望Form2在Form3之前可见,因为我希望在执行Form3操作(包括在Form2上编辑标签1)时,Form2显示label1的每次更新(如"Doin:operation 1",然后"Doin:operation 2",依此类推)。
我还尝试在Form2上制作一个按钮(Button1),定义Button1_nClick()事件,并用"创建并显示Form3"来实现其内容。但是,当我单击按钮1时,Form2"消失"(就像Windows应用程序被阻止并变为"暂停"时一样),它只在Form3出现的同一时刻显示最终标签("操作:100")。
我的问题摘要:-->我有3张表格
- 表格1
- 表格2(带标签1)
- Form3(在Form2中编辑Label1)
-->应该发生什么:
- Form1创建并显示Form2
- Form2创建并显示Form3
- Form3进行各种操作(如创建列表、排序等),每次操作都会更新Form2中的Label1(用户必须查看更新过程)
所有Form2操作都是从Form2构造函数中调用的函数调用的,如:Form2_Constructor()=>调用makeOperation()-->makeOperations()=>调用Operation1(),然后调用Operation2(),最后调用Operation3(),其中每个OperationX()包含一个循环(比如说30次迭代),执行一些操作并调用Form2.setLabel1("操作名称");
我的想法(正如标题所说)是制作两条不同的线。。..或者至少找到一种方法,让两个窗体在"并行模式"下运行(这样,当Form3执行操作时,Form2就不会空闲)。
知道怎么解决这个问题吗?
如果您真的想要两个不同的表单在不同的线程上运行,那么您将需要创建另一个通过Application.Run
运行消息循环的线程。但是,不建议采用这种方法。这可能会导致奇怪的问题,并且会使两种不同的表单不方便相互访问,因为从一种表单到另一种表单的所有访问都需要使用Control.Invoke
进行封送处理。
我建议使用单一的UI线程。将当前由Form3
执行的长时间运行的代码获取到工作线程中。随着工作线程的进展,您可以缓慢地将结果发布到Form3
。可以通过以下两种方式之一将结果发布到Form3
。
- 让工作线程通过调用
Invoke
或BeginInvoke
将结果推送给Form3
- 让
Form3
轮询共享数据结构以获取工作线程发布的结果
那些监视我答案的人已经知道我要推荐哪一个:UI线程轮询结果的稍后方法。它具有以下优点。
- 它打破了
Control.Invoke
强加的UI和工作线程之间的紧密耦合 - 它将更新UI的责任放在UI线程上,不管怎样,它都应该属于该线程
- UI线程可以决定更新的时间和频率
- UI消息泵没有被溢出的风险,就像工作线程启动的封送处理技术一样
- 工作线程在继续执行下一步之前不必等待更新已执行的确认(即,在UI和工作线程上都可以获得更高的吞吐量)
如果Form3
当前正在执行的工作无法轻松地移动到工作线程(也许它在大多数时候都在操纵UI控件,而这只能通过承载表单的UI线程来完成),那么您将需要放慢执行此工作的速度,以便表单有更多的时间响应用户输入。这意味着你将被迫做出一些牺牲。例如,您可能需要一次只填充一百行,而不是用数千行填充网格,并提供某种分页行为,允许用户移动到下一个一百行。
通常,您应该将所有UI保留在主线程上,并在后台执行长时间运行的任务。
根据您使用的.Net版本以及后台工作的内容,您可能会使用BackgroundWorker、ThreadPool或.Net 4.0 Task类。
无论你使用哪一个,当你更新UI中的状态时,你都必须使用控件的Invoke方法来更新它,否则你会遇到一个异常,即试图从一个没有创建控件的线程更新控件。
有许多关于后台线程和更新UI的文章。
这是一篇关于.Net 4任务和WinForms的文章(完全公开,这是我的文章)。
这是一个模拟您所描述的基本VS项目——三个表单相互启动,第三个表单执行后台工作并更新表单2上的状态(后台工作由BackgroundWorker控件完成)。
是的。坐下来编程。启动另一个线程,在其中创建另一个表单并启动调度器。然后使用标准的消息传递语义在表单之间进行对话。