工作线程如何与主UI线程通信?

时间:2013-10-02 18:29:18

标签: c++ multithreading mfc

工作线程与主UI线程通信的最佳方式是什么?

总结:我的C ++ / MFC应用程序是基于对话框的。要进行冗长的计算,主UI线程会创建多个工作线程。当工作线程在计算中进展时,它们会将进度报告给主UI线程,然后显示进度。

这适用于数字进度值,它们位于共享内存中(由工作人员编写,由UI读取),但我遇到了文本进度消息的问题。我尝试过的解决方案已经过了几次迭代,似乎都没有。

  1. 我让UI线程传递指向工作人员控件的指针,工作人员直接更新了UI。这不是很有效,似乎是错误的做法。

  2. 我让工作人员使用SendMessage向UI线程的窗口发送消息。这陷入僵局。 (在处理完邮件之前,SendMessage不会返回。)

  3. 与(2)相同,只是将PostMessage用于UI线程的窗口。这工作了一段时间,然后消息丢失了。 (PostMessage立即返回。)进一步调查显示,超出了消息队列的配额(默认为10,000)。

  4. 我增加了邮件队列的配额(注册表中的变量HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ Windows NT \ CurrentVersion \ Windows \ USERPostMessageLimit),但丢失的邮件数没有变化。

    < / LI>
  5. 我将每个工作线程缓冲区消息放在4 KB缓冲区中,并在缓冲区填充时使用PostMessage。这失败了,因为UI线程从未收到任何消息。当我将缓冲区大小增加到64 KB时也是如此。

  6. 工作线程以“最低”优先级运行,UI线程以“正常”优先级运行。工作线程正在发送带有

    等代码的消息
    UIMessage *pUI=new UIMessage; // so it won't go out of scope (main dialog will delete it)
    pUI->buffer=traceLineBuffer; pUI->nOutputs=traceN;
    BOOL ok=::PostMessage(hWndMainDlg,TraceLineMsg,(WPARAM)pUI, NULL/*lParam*/);
    

    并且UI正在使用类似

    的代码接收它们
    BEGIN_MESSAGE_MAP(CMainDlg, CDialog)
    ...
    ON_MESSAGE(TraceLineMsg,OnTraceLineMsg)
    ...
    END_MESSAGE_MAP()
    
    LRESULT CMainDlg::OnTraceLineMsg(WPARAM wParam, LPARAM lParam)
    {
        UIMessage *pUI=(UIMessage *)wParam;
        char *p=pUI->buffer;
        // PROCESS BUFFER
        delete[] pUI->buffer;
        delete pUI;
        return 0;
    }
    

    问题:

    1. 在可能有数千条文本报告爆发的情况下,工作人员发布进度报告的首选方式是什么?

    2. 为什么我不能增加队列中帖子消息的配额?

    3. 为什么主UI线程似乎永远不会收到缓冲区中的消息,即使传输它们的机制与发布单个报告相同?

    4. 64位Windows 7,Visual Studio 2010,本机C ++ / MFC

3 个答案:

答案 0 :(得分:3)

使用WaitForMultipleObjects调用中的主线程,不会处理任何消息,也不会更新任何控件或其他窗口。解决方案是:不要那样做。

答案 1 :(得分:1)

在GUI上发布进度报告没有太多意义,比用户可以同意的更快。当其他线程中存在大量活动时,通常使用GUI计时器轮询线程中的进度变量,因此每次更新GUI控件,比如500ms。

这是计时器轮询实际上有利的极少数时间之一。您可以获得peridoc进度报告,而不会有更新GUI GUI消息队列的危险。例如,uTorrent客户端(其中存在大量网络活动)使用此方案 - 尝试更新收到的每个网络协议单元上的GUI下载统计信息肯定会填充GUI。

你在(5)中的缓冲方案应该有效。我经常通过将对象指针加载到LPARAM或WPARAM中来将大数据项传输到主GUI线程,在工作线程中新建它们并在显示后在GUI中删除它们。你的(5)应该有效,至少减少了进度数据传输的开销。我只能假设要显示的数据量仍然太大,因此GUI线程仍然无法跟上:(

答案 2 :(得分:1)

Windows上的MFC工作线程有多个与主线程通信的选项。您拥有标准线程信号和同步primitives(互斥,信号量,event),易于使用的PostMessage和更高性能的I/O Completion Port机制。

// syncronization
{
    CSingleLock lock(&sharedCriticalSection,TRUE);
    sharedList.push_back(msg);
}
// other thread(s) are blocked/pending or you send an event or message to signal

// messages
Data* data = new Data(payload);
PostMessage(hWnd, REGISTERED_MESSAGE, 0, (LPARAM)data);
// target window handles message and deletes data 
// if it is not blocked or too slow and the queue overflows

// skipping lots of IO completion port boilerplate and showing the key methods
messagePort = CreateIoCompletionPort(...);
...
GetQueuedCompletionStatus(messagePort,...);
...
PostQueuedCompletionStatus(messagePort,...);

如果您阻止或忙碌等待线程完成,他们都不会做太多工作或提高您的性能或响应能力。

对你观察的评论:

  1. 不要让工人触摸GUI。
  2. 不要使用工作线程中的SendMessage。
  3. 只要用户界面可以跟上并且不被阻止,PostMessage适用于低容量。
  4. 如果您认为这需要改变,您应该重新考虑您的解决方案。发送更少或使用更高性能的选项。
  5. 如果用户界面没有被阻止,合并消息可能会有所帮助。
  6. 您的问题的答案:

    1. 重新评估是否真的需要一次性发送数千封邮件。用户需要每秒多少次更改?如果您需要全部发送它们,请查看I / O完成端口机制。
    2. 我不会尝试,除了......之外它可能有用。
    3. 您的主UI线程被阻止,等待工作人员在WaitForMultipleObjects中完成,而您的工作人员和其他被阻止的事件必须生成超过最大队列消息。
相关问题