作者:陆麟
转载请征得作者同意.
2002.1.14
有时候需要一些比较古怪的代码, 来达到一些很普通的效果. 例如, 在自己的消息处理函数中要创建一个新的线程,
而新的线程必须创建新的窗口, 并且等窗口完全出现后, 原来的消息处理函数将进行一项长时间的计算活动.
在MFC的代码中通过普通的方案无法实现这样的要求.
例如, 通常的作法是
void CLASSX::OnOK( )
{
//
//创建线程, 并跑起来.
//
CSOMETHREAD *Step = (CSOMETHREAD *)AfxBeginThread(RUNTIME_CLASS(CSOMETHREAD));
while (Step->InternalFlag != READY)
{
//等到线程把InternalFlag
设置为 READY
Sleep(1000);
}
//开始长时间计算
Compute(X);
//计算结束, 线程也要结束
Step->Stop();
}
如果CSOMETHREAD是个WORKER线程, 没有问题, 但是如果CSOMETHREAD将有USER
INTERFACE. 那可糟糕. 什么东西都不会被显示出来. 而且while可能会进入死循环.
因为如果InternalFlag是在窗口创建完成后设立的, 那就根本是个DeadLock. OnOk等待InternalFlag为READY,
而线程因为创建窗口时, MFC本身的MESSAGE LOOP没有返回, 根本不能处理新的窗口的消息.
导致新窗口的WM_CREATE等一系列消息无法处理, 窗口创建不能完成... 尽管新线程本身的Run函数处理中使用了标准的
MSG msg;
while (GetMessage(&msg,0,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
UI消息派发, 但是因为创建窗口没有完成, 没有办法执行到Run函数处, 这个消息循环还用不到.
OK, Inside Programming现在给出一种替代方案. 无效重新设计程序结构, 使用临时消息处理器就能完美解决此问题.
我们要干的是在while循环中使用自己的消息循环. 看下面的代码:
void CLASSX::OnOK( )
{
//
//创建线程, 并跑起来.
//
CSOMETHREAD *Step = (CSOMETHREAD *)AfxBeginThread(RUNTIME_CLASS(CSOMETHREAD));
while (Step->InternalFlag != READY)
{
//等到线程把InternalFlag
设置为 READY
MSG msg;
if (GetMessage(&msg,0,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
//开始长时间计算
Compute(X);
//计算结束, 线程也要结束
Step->Stop();
}
不使用Sleep()函数, 用自己的临时的消息派发, 就能完美解决问题. 当InternalFlag被设置时, 意味着新线程的Run已经被执行到. 新线程自己的消息派发已经开始工作. 这样, 死循环就消除了.:) 此示例代码极度适用于MFC. 对很多架构不完善导致的UI问题可以解决.