
界面线程与消息循环
调用窗口处理回调 每个消息(如鼠标事件、键盘事件等)都有对应的控件处理逻辑。
第1原则:避免长时间占用界面事件处理。 第2原则:长任务不能放在主线程,使用工作线程代替。
第3原则:在工作线程中不能直接更新 UI 组件,必须通过 正确做法:使用
发布日期:2021-05-14 14:58:43
浏览次数:17
分类:精选文章
本文共 3781 字,大约阅读时间需要 12 分钟。
界面线程与消息循环
消息循环
消息与消息循环是 GUI 开发中的核心概念。在 Windows Form 开发中,所有的界面消息都在一个持续运行的 while 循环内处理。这个循环不断地获取消息并逐一处理它们。
消息循环的工作原理可以通过以下伪代码简要体现:
while (message = GetMessage()) { // 依次处理 message...}
在实际编程中,消息循环通常由 Application.Run()
方法启动。例如:
Application.Run(new Form1());
具体来说,消息处理遵循以下步骤:
WndProc
,以便在消息循环中进一步分发消息。所有界面事件回调实际上都在消息循环中运行,因此需要确保它们能够及时返回,避免阻塞消息循环。这是确保界面不卡顿的关键。
界面卡顿
当一个按钮的点击事件处理程序占用较长时间(例如多秒)时,界面会显得卡顿或不响应。原因在于消息循环无法及时处理下一个消息。
解决方法
- 第1原则:确保所有消息处理回调都能快速返回。处理时间过长会导致界面卡顿,建议不超过 300 毫秒。
- 第2原则:耗时较长的任务应放入工作线程中执行。
工作线程
为了处理耗时较长的任务,例如查询数据库、上传下载或编解码,可以使用工作线程。工作线程可以在后台运行任务,同时保证界面不卡顿。
示例:创建工作线程
public partial class Form1 : Form{ public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { Thread th = new Thread(new ThreadStart(this.Execute)); th.Start(); } private void Execute() { Console.WriteLine("开始执行任务..."); Thread.Sleep(5000); // 模拟长时间处理 Console.WriteLine("任务完成"); }}
- 界面线程:处理界面事件和消息循环。
- 工作线程:负责长时间的后台任务。
原则总结:
界面更新
使用工作线程更新界面时,需遵循以下规则:
Invoke
方法上报消息到主线程。Invoke
推送自定义消息到消息循环中,让主线程执行相应的 UI 更新逻辑。错误的实现示例:
using System;using System.Threading;public partial class Form1 : Form{ private void button1_Click(object sender, EventArgs e) { Thread th = new Thread(new ThreadStart(this.Execute)); th.Start(); } private void Execute() { this.TextBox1.AppendText("3..\r\n"); Thread.Sleep(1000); this.TextBox1.AppendText("2..\r\n"); Thread.Sleep(1000); this.TextBox1.AppendText("1..\r\n"); Thread.Sleep(1000); this.TextBox1.AppendText("OK..\r\n"); }}
- 错误原因:直接在工作线程中操作 UI 组件会导致 HWND 键盘损坏,会报错。
- 正确实现:通过
Invoke
方法将更新操作推送到主线程。
Action与Func
Action
和 Func
是常用的委托类型,用于定义接收单个参数或多个参数的方法。
Action<T>
:适用于返回值为void
的方法。Func<T1, T2, ... , Tn>
:适用于返回值不为void
的方法。
示例:
public partial class Form1 : Form{ public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { Thread th = new Thread(new ThreadStart(this.Execute)); th.Start(); } private void Execute() { this.Invoke(new Action(this.ShowProgress), "3..\r\n"); Thread.Sleep(1000); this.Invoke(new Action (this.ShowProgress), "2..\r\n"); Thread.Sleep(1000); this.Invoke(new Action (this.ShowProgress), "1..\r\n"); Thread.Sleep(1000); this.Invoke(new Action (this.ShowProgress), "OK..\r\n"); } public void ShowProgress(string text) { this.TextBox1.AppendText(text); }}
InvokeRequired
Control.InvokeRequired
用于检查当前线程是否是 GUI 线程。
public partial class Form1 : Form{ public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { Thread th = new Thread(new ThreadStart(this.Execute)); th.Start(); } private void Execute() { ShowProgress("3\r\n"); Thread.Sleep(1000); ShowProgress("2\r\n"); Thread.Sleep(1000); ShowProgress("1\r\n"); Thread.Sleep(1000); ShowProgress("OK\r\n"); } public void ShowProgress(string str) { if (this.InvokeRequired) { Console.WriteLine("Call In Work Thread : " + str); this.Invoke(new Action(this.ShowProgress), str); } else { Console.WriteLine("Call In Message Loop : " + str); this.TextBox1.AppendText(str); } }}
总结
通过以上优化,可以更好地理解和应用 Windows Form 的多线程开发原理。遵循各个原则可以有效避免界面卡顿,并确保 UI 更新的正确性。
发表评论
最新留言
做的很好,不错不错
[***.243.131.199]2025年04月19日 16时47分50秒
关于作者

喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
c++之程序流程控制
2019-03-06
spring-boot-2.0.3之redis缓存实现,不是你想的那样哦!
2019-03-06
httprunner学习23-加解密
2019-03-06
有道云笔记 同步到我的博客园
2019-03-06
李笑来必读书籍整理
2019-03-06
http头部 Expect
2019-03-06
Hadoop(十六)之使用Combiner优化MapReduce
2019-03-06
《机器学习Python实现_10_06_集成学习_boosting_gbdt分类实现》
2019-03-06
CoreCLR源码探索(八) JIT的工作原理(详解篇)
2019-03-06
IOS开发Swift笔记16-错误处理
2019-03-07
flume使用中的一些常见错误解决办法 (地址已经使用)
2019-03-07
andriod 开发错误记录
2019-03-07
C语言编译错误列表
2019-03-07
看明白这两种情况,才敢说自己懂跨链! | 喵懂区块链24期
2019-03-07
张一鸣:创业7年,我经历的5件事
2019-03-07
git拉取远程指定分支代码
2019-03-07
《web安全入门》(四)前端开发基础Javascript
2019-03-07