当应用程序的主线程忙时,应用程序的PostMessage消息似乎丢失了

时间:2012-05-20 23:48:43

标签: c++ multithreading synchronization c++builder postmessage

我正在编写一个多线程程序,它使用消息传递来在线程之间进行通信。我正在使用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,而它可能会被暂停而不会这样做。

0 个答案:

没有答案