我正在编写一个多线程程序,它使用消息传递来在线程之间进行通信。我正在使用Embarcadero RAD Studio C ++ Builder 2010.这些消息使用PostMessage发送到Application的句柄,然后由函数处理(来自主窗体构造函数的代码:Application->OnMessage = CatchMessage;
)其中CatchMessage
是一种方法主要形式。 CatchMessage包含一些要执行的代码(比如唤醒另一个线程,请参阅下面的代码)。
这是我的问题。每当你抓住一个窗口移动它时,窗口的内容就会“冻结”,直到你放开窗口或开始移动它。显然,应用程序的主线程被挂断了;并且我在这段时间内发送的所有消息都“丢失” - 其中的代码永远不会被执行。
下面是我的代码(有一个主线程,用于绘图的线程(从像素数组到内部位图)和用于渲染的线程(填充像素数组)。它们按以下方式同步:渲染器创建一个新帧循环,触发一个布尔值,指示有一个新帧,如果抽屉完成绘制前一帧,则发送一个消息,交换两个缓冲区(在我的应用程序的上下文中必要的东西)并唤醒正在睡觉的抽屉;然后等待直到有一个确认缓冲区是交换的;如果抽屉仍然忙于前一帧,渲染器只是开始制作另一帧。抽屉在被唤醒时,声明它正忙,绘制帧,发送一条触发memcpy的消息Form的Canvas(线程安全,因为它是从主线程完成的),表明它已准备就绪并进入休眠状态(99%的情况下绘制比渲染更快,因此这个措施是为了节省一些CPU时间)。
问题是,当我抓住窗户移动它时,表格上的运动图像停止。向所有线程发送暂停信号(不停止它们,只触发它们在每个周期检查的布尔值)使其中一个退出,但另一个继续前进(我在任务管理器中看到一个核心100%忙) 。我把这个问题缩小到消息发送:当来自Renderer的消息“丢失”并且其处理程序的代码从未执行时,会发生两件事:1)抽屉线程永远不会被唤醒; 2)渲染线程陷入无限循环,等待缓冲区交换的确认。
代码:
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
Application->OnMessage = CatchMessage;
}
bool TimeToStop;
DWORD WINAPI DrawLoop(LPVOID params)
{
while(!TimeToStop)
{
GE.DrawerReady = 0;
GE.FrameCounter++;
GE.DrawToBitmap();
PostMessage(Application->Handle, BITMAP_READY, 0, 0);
GE.DrawerReady = 1;
SuspendThread(DrawLoopHandle);
}
return 0;
}
DWORD WINAPI PseudoRenderLoop(LPVOID params)
{
while(!TimeToStop)
{
GE.GenerateDemoFrame();
if(GE.DrawerReady)
{
GE.BuffersAreSwitched = 0;
SendMessage(Application->Handle, RENDER_READY, 0, 0);
while(!GE.BuffersAreSwitched){}
}
}
return 0;
}
void __fastcall TForm1::Button1Click(TObject *Sender) // "Go / Pause" button
{
if(TimeToStop == 1)
{
TimeToStop = 0;
DrawLoopHandle = CreateThread(NULL, 0, DrawLoop, NULL, 0, NULL);
PseudoRenderLoopHandle = CreateThread(NULL, 0, PseudoRenderLoop, NULL, 0, NULL);
}
else
{
TimeToStop = 1;
}
}
void __fastcall TForm1::CatchMessage(tagMSG &Msg, bool &Handled)
{
if(Msg.message == BITMAP_READY)
{
Form1->Invalidate();
GE.FrameCounter++;
}
else
{
if(Msg.message == RENDER_READY)
{
GE.SwitchRenderBuffers();
ResumeThread(DrawLoopHandle);
GE.BuffersAreSwitched = 1;
}
}
}
当{I}占用窗口时由于主线程忙而导致SendMessage(Application->Handle, RENDER_READY, 0, 0);
丢失时,缓冲区的更改,确认以及Drawer线程的唤醒都不会发生,因此Drawer线程永远被挂起并且渲染器线程卡在循环while(!GE.BuffersAreSwitched){}
中。但是,奇怪的是,当我点击暂停时,我仍然看到一个(两个)线程退出,这是意料之外的,因为当一个被挂起而另一个被挂起时,它们都无法到达return 0;
代码。
我绝对相信这种行为是由丢失的消息引起的,因为当我用一些布尔值替换通信时(而(!变量){}而不是SuspendThread等)一切都按预期开始工作。我的问题:当主线程繁忙时,如何确保消息不会丢失?使用布尔值的解决方案仍然有效,但这样等待的Drawer线程会占用CPU,而它可能会被暂停而不会这样做。