——Windows GUI程序中使用线程的心得体会
①避免在工作者线程中使用SendMessage,改用PostMessage比较好。
原因是这样的,以一个普通程序退出为例,我们经常会使用以下方式构建退出逻辑:
主线程正在消息处理函数 | 工作者线程正在处理任务 |
void OnDestroy(){//WM_DESTROY | void DoWork(){ |
如上所示,只要主线程开始处理WM_DESTROY消息后,只有等到OnDestroy返回,主线程才会处理下一个Windows消息。一旦OnDestroy函数开始执行且还没有返回,DoWork中发出的WM_GETTEXT消息就不会被主线程处理,使用SendMessage后工作者线程就会等待主线程处理WM_GETTEXT进入休眠,注意这种休眠无法使用ResumeThread唤醒(即使唤醒只要主线程没有处理WM_GETTEXT消息也会继续睡过去)。而这时OnDestroy函数正在用WaitForSingleObject等待DoWork检测m_hStop信号退出线程,所以两者互相等待都进入了休眠,这样就产生了死锁。
很多朋友一定会说,那就把OnDestroy里WaitForSingleObject去掉直接往后执行不就没有死锁了么?死锁是没有了,由于OnDestroy函数的主要职责就是负责释放资源,接下来它一定会调用类似CWnd::OnDestroy()函数来销毁窗口资源,这样一来DoWork里就很有可能因为访问界面资源而崩溃。
所以,②应该避免在工作者线程中直接访问界面资源,包括不要使用GetWindowsText等函数。可以把要显示到界面上的数据,放进一个自己构建的队列中,然后使用PostMessage发出特定的消息或者用户自定义的消息,由消息处理函数去完成。
③如果必须在线程里访问界面资源怎么办?可以使用类似MFC中的界面线程。
下面是我在工作者线程里直接访问界面资源的方法,利用消息泵:
工作者线程正在处理任务 |
void DoWork(){ |
上面也许不是解决退出死锁的万能方法,只是我的一点心得,希望大家有更好的办法也贴上来。