PostMessage与SendMessage各自的问题
本文将使用C++语言,在MFC框架的配合下给出PostMessage、SendMessage等的使用方式与使用不当造成的后果(讨论均针对自定义的消息进行)。如有什么错误,欢迎指正。
写过Windows程序的同学都知道PostMessage、SendMessage的区别,PostMessage函数调用发送之后,立即返回,不等待消息处理完成。而SendMessage则让调用的线程处于阻塞(BLOCk)状态,直到消息处理完成。
正由于这两个函数的区别导致了如下想法:
想法1:PostMessage立即返回,在程序中,处理界面显示(如处理进度条、滚动条等)时使用PostMessage,不会影响程序的用户体验。
想法2:在程序中全用PostMessage,放弃SendMessage,好处:PostMessage是立即返回的,可以不影响程序的正常流程,就算在消息处理函数中卡死了,也不影响主线程的运行。
起初“学习”到了这些想法,以为受益匪浅,但经过一段时间之后,发现此两种想法都是不可取的。
分析想法1:
这里可分为两点:
1) 在主线程中Post消息,以处理进度条显示(用WM_MY_TEST的参数WPARAM、LPARAM来处理进度条的显示)
代码:code_1
#define WM_MY_TEST (WM_USER + 100)
void CMyDlg::OnBnClickedOk()
{
int nParam1 = 0;
int nParam2 = 0;
for (int nIndex = 0; nIndex < 1000; nIndex++)
{
// Do other things
// …
nParam1++;
nParam2++;
PostMessage(WM_MY_TEST, (WPARAM)&nParam1, (LPARAM)&nParam2);
}
//OnOK();
}
LRESULT CMyDlg::OnMyTest(WPARAM wParam, LPARAM lParam)
{
static int nTimes = 0;
CString strOutPut;
int* pParam1 = (int*)wParam;
int* pParam2 = (int*)lParam;
nTimes++;
strOutPut.Format(_T("%s%d %s%d %s%d"),
_T("Param1 = "), *pParam1,
_T("Param2 = "), *pParam2,
_T("RealTimes = "), nTimes);
OutputDebugString(strOutPut);
return 0;
}
code_1将运行的结果:
Param1 = 0 Param2 = 0 RealTimes = 1
Param1 = 0 Param2 = 0 RealTimes = 2
Param1 = 0 Param2 = 0 RealTimes = 3
…
Param1 = 0 Param2 = 0 RealTimes = 1000
结果远不如我们所料,表现为PostMessage多次发送时,2~1000的消息参数全被冲掉了。用pParam1、pParam2来处理进度条的话,后果可想而知(进度条根本没动)。如果将上面的PostMessage改为SendMessage,结果如下:
Param1 = 1 Param2 = 1 RealTimes = 1
Param1 = 2 Param2 = 2 RealTimes = 2
Param1 = 3 Param2 = 3 RealTimes = 3
…
Param1 = 1000 Param2 = 1000 RealTimes = 1000
可见,稳定的输出了需要的内容,可以很好的控制。
在此情况下(主线程中Post消息时),不仅没有改善用户体验,反而更差了。
不可以频繁使用PostMessage发送同一个消息,除非保证上一次发送的消息被处理完成(这如何保证???),这还不如直接用SendMessage。
当然OnMyTest函数可能是这样的:
LRESULT CMyDlg::OnMyTest(WPARAM wParam, LPARAM lParam)
{
static int nTimes = 0;
CString strOutPut;
int* pParam1 = (int*)wParam;
int* pParam2 = (int*)lParam;
nTimes++;
strOutPut.Format(_T("%s%d %s%d %s%d"),
_T("Param1 = "), *pParam1,
_T("Param2 = "), *pParam2,
_T("RealTimes = "), nTimes);
OutputDebugString(strOutPut);
// 大量访问网络,磁盘等低速操作
return 0;
}
在这种情况下,如果用SendMessage的话,用户体验将会大大下降,甚至导致程序无法响应。于是有人提出了使用PostMessage,这样程序不会无法响应,最多显示不正确罢了。乍一看,提议似乎还不错,至少程序正常运行了。但是,这些网络访问、磁盘读写等操作为什么要放到界面的代码中呢?界面、代码分离才是合理的,因此可以认定,访问网络、磁盘读写等操作不应该放到这里来处理。
2) 在非主线程中Post消息,以处理进度条显示(用WM_MY_TEST的参数WPARAM、LPARAM来处理进度条的显示)
代码:code_2
DWORD WINAPI ThreadProc( LPVOID lpParam )
{
CMyDlg *pThis = (CMyDlg *)lpParam;
int nParam1 = 0;
int nParam2 = 0;
for (int nIndex = 0; nIndex < 1000; nIndex++)
{
nParam1++;
nParam2++;
pThis->PostMessage(WM_MY_TEST, (WPARAM)&(nParam1), (LPARAM)&(nParam2));
}
return 0;
}
void CMyDlg::OnBnClickedOk()
{
HANDLE hThread = CreateThread(NULL,
0,
ThreadProc,
(void*)this,
0,
NULL);
//OnOK();
}
LRESULT CMyDlg::OnMyTest(WPARAM wParam, LPARAM lParam)
{
static int nTimes = 0;
CString strOutPut;
int* pParam1 = (int*)wParam;
int* pParam2 = (int*)lParam;
nTimes++;
strOutPut.Format(_T("%s%d %s%d %s%d"),
_T("Param1 = "), *pParam1,
_T("Param2 = "), *pParam2,
_T("RealTimes = "), nTimes);
OutputDebugString(strOutPut);
return 0;
}
code_2的运行结果:
(程序直接崩溃了)
线程函数不等待WM_MY_TEST的返回,循环1000次之后直接退出了,这导致栈上的变量nParam1、nParam2被释放,然后OnMyTest处理的时候,nParam1、nParam2的地址已经无效了,导致崩溃。SendMessage则不会出现此类情况。
修改程序
代码:code_2(2)
DWORD WINAPI ThreadProc( LPVOID lpParam )
{
CqwerDlg *pThis = (CqwerDlg *)lpParam;
int *nParam1 = NULL;
int *nParam2 = NULL;
nParam1 = new int;
nParam2 = new int;
for (int nIndex = 0; nIndex < 1000; nIndex++)
{
*nParam1 = nIndex;
*nParam2 = nIndex;
pThis->PostMessage(WM_MY_TEST, (WPARAM)nParam1, (LPARAM)nParam2);
}
return 0;
}
由于堆内存没有被释放,所以程序没有崩溃,在我的机器上运行结果为:
Param1 = 27 Param2 = 27 RealTimes = 1
Param1 = 117 Param2 = 117 RealTimes = 2
Param1 = 162 Param2 = 162 RealTimes = 3
Param1 = 218 Param2 = 218 RealTimes = 4
Param1 = 272 Param2 = 272 RealTimes = 5
Param1 = 312 Param2 = 312 RealTimes = 6
Param1 = 353 Param2 = 353 RealTimes = 7
Param1 = 391 Param2 = 391 RealTimes = 8
Param1 = 431 Param2 = 431 RealTimes = 9
程序执行非常不稳定,每次结构都不同,当然也不能用这些数据了。当把两个new int放到for循环中,执行结果是稳定的,但这样的代码晦涩难懂。在这里用PostMessage没有任何好处,所以建议使用SendMessage。
分析想法2:
1) 已知一个线程处理了A,由于其他需要,此线程还需要处理B(必须在A完成之后)。需要新加入代码来实现,以前的代码为:
代码:code_3
DWORD WINAPI ThreadProc(LPVOID lpParam)
{
HWND hWnd = (HWND)lpParam;
// Do some things
::PostMessage(hWnd, WM_MUST_DO_THING_A, 0, 0);
return 0;
}
LRESULT CMyDlg::OnMustDoThingA(WPARAM wParam, LPARAM lParam)
{
Do some things for A
Do some things for B // 费解,这是A的处理函数!!!
}
我们可以多加个消息,WM_MUST_DO_THING_B,然后用PostMessage发送,哦,不能这样,B一定要在A完成之后,现在唯一的处理方式只有对B的处理加入到A的消息处理函数中,这将导致费解的代码。如果在原来的线程函数中PostMessage为SendMessage,则不会如此。
如果A、B是不相关联的两个操作,为了以后扩展,也不该用PostMessage,这种情况下应该多创建一个线程进行处理。
2) 对于需要处理的比较重要的操作(这些可能导致卡死):
LRESULT CMyDlg::OnDoThing(WPARAM wParam, LPARAM lParam)
{
Things To do. // 这里可能会卡死,但又必须处理
}
在这种情况下,建议使用SendMessageTimeout,当等待一段时间后,消息仍然没有处理完成,则程序放弃操作继续运行。
3)对于所有无关紧要的操作:
这些操作包括:清理磁盘临时文件等等,这些操作有没正常处理,程序并不关心,在这种情况下,则可使用PostMessage、
终上所述,我们得到如下结论:
1、 PostMessage不能频繁的发送同一个消息,除非保证上次Post过的消息处理完成。
2、 如果用SendMessage导致应用程序用户体验下降,应该检查消息处理函数,而不仅仅简单改为PostMessage。
3、 如果消息是程序必须处理的,则不能使用PostMessage。
4、 如果消息是程序必须处理,而又有可能导致程序卡死,则使用SendMessageTimeout。
5、 如果消息是无关紧要的,则可以建议使用PostMessage。
6、 对于WM_HOTKEY 等Windows特定的消息,则只能使用PostMessage(未在本文中说明)。
参考:http://blog.csdn.net/xt_xiaotian/article/details/2778689
PostMessage与SendMessage各自的问题的更多相关文章
- [C#.net]PostMessage与SendMessage的区别
用 PostMessage.SendNotifyMessage.SendMessageCallback 等异步函数发送系统消息时,参数里不可以使用指针,因为发送者并不等待消息的处理就返回,接受者还没处 ...
- PostMessage与SendMessage的区别(二)
在做基于窗口的Windows程序的时候,我们避免不了要向窗口发送消息,有两种方式,一种是PostMessage,另外一种是SendMessage.关于这两个宏,我是通过狠狠的看MSDN才搞明白的,那里 ...
- MFC发送自定义消息-PostMessage和SendMessage
PostMessage:把消息投放到线程的消息队列,不能消息被处理就立即返回SendMessage:消息被处理完后才返回 几种发送消息的写法: ::PostMessage(GetSafeHwnd( ...
- PostMessage和SendMessage的区别
1, PostMessage只把消息放入队列,不管其他程序是否处理都返回,然后继续执行,这是个异步消息投放函数.而SendMessage必须等待其他程序处理消息完了之后才返回,继续执行,这是个同步消息 ...
- postmessage and sendmessage
从msdn上看二者的解释: postmessage : Places (posts) a message in the message queue associated with the thread ...
- postMessage和sendMessage
参考:http://www.cnblogs.com/giggle/p/5350288.html(浅谈webWorker) http://blog.csdn.net/zha_zi/article/det ...
- PostMessage与SendMessage的区别
PostMessage只负责将消息放到消息队列中,不确定何时及是否处理 SendMessage要等到受到消息处理的返回码(DWord类型)后才继续 PostMessage执行后马上返回 SendMes ...
- VC++中PostMessage、SendMessage和PeekMessage之间的区别
1, PostMessage只把消息放入队列,不管其他程序是否处理都返回,然后继续执行,这是个异步消息投放函数.而SendMessage必须等待其他程序处理消息完了之后才返回,继续执行,这是个同步消息 ...
- MFC窗口消息PostMessage和SendMessage
以前这些消息用得比较少,但是今天碰到了个事儿,我看非用消息不可. 事情是这样的,我在线程中需要刷新对话框上面的内容,但是每每执行到UpdateData时就出现了断言错误. 查了相关资料,发现这个可能是 ...
随机推荐
- 浅析Mysql 数据回滚错误的解决方法
介绍一下关于Mysql数据回滚错误的解决方法.需要的朋友可以过来参考下 MYSQL的事务处理主要有两种方法.1.用begin,rollback,commit来实现begin 开始一个事务rollbac ...
- spring boot单元测试(转)
Junit这种老技术,现在又拿出来说,不为别的,某种程度上来说,更是为了要说明它在项目中的重要性.凭本人的感觉和经验来说,在项目中完全按标准都写Junit用例覆盖大部分业务代码的,应该不会超过一半. ...
- ThreadLocal模式探索
一.首先,ThreadLocal模式使共享数据能多个线程被访问,每个线程访问的只是这个数据的副本,线程之间互不影响. 例子1: package Thread2; public class Counte ...
- Global::validateEmail
/***************************************************************** (C) Copyright DENTSPLY Internatio ...
- openerp模块收藏 移除下拉选择列表中的“创建并编辑”链接(转载)
移除下拉选择列表中的“创建并编辑”链接 原文:http://shine-it.net/index.php/topic,5990.0.html 有时希望下拉列表中列出的项是与主表某个字段关联的,用户只能 ...
- 拥抱ARM妹纸第二季 之 第三次 给我变个月亮,让约会更浪漫!
嗯嗯,效果不错.趁着这个热乎劲,接到俺的LED测试板上试试.呃~~~ 竟然和小LED的效果不一样啊,不一样.不但闪烁而且完全没有调光效果.郁闷内,查查原因呗.看看那里出问题.迅速在PT4115手册里翻 ...
- Java Day 06
二维数组 定义: 格式1 int[][] arr = new int[3][2]; 格式2 int[][] arr = new int[3][];//每个一维数组初始化时为null 空指针异常 格式3 ...
- 顺序容器:vector,deque,list
1.顺序容器:vector,deque,list 容器类共享公共接口,只要学会其中一种类型就能运用另一种类型.每种容器提供一组不同的时间和功能这种方案,通常不需要修改代码,秩序改变类型声明,每一种容器 ...
- 论坛类应用双Tableview翻页效果实现
作为一名篮球爱好者,经常使用虎扑体育,虎扑体育应用最核心的部分就是其论坛功能,无论哪个版块,论坛都是其核心,而其论坛部分的实现又别具一格,它以两个tableview的形式翻页滚动显示,而不是常见的那种 ...
- python 发邮件 utf-8
import smtplib from operator import itemgetter, attrgetter from email.mime.text import MIMEText from ...