首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >工作线程如何与主UI线程通信?

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

Stack Overflow用户
提问于 2013-10-02 18:29:17
回答 3查看 5.2K关注 0票数 5

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

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

这对于共享内存中的数值进度值(由工作人员编写,通过UI读取)很好,但我在处理文本进度消息时遇到了问题。我尝试过的解决方案经历了几次迭代,但似乎没有一次有效。

  1. 我让UI线程将指向控件的指针传递给工作人员,并且工作人员直接更新了UI。这并不是很有效,而且似乎是错误的方法。
  2. 我让工作人员使用SendMessage向UI线程窗口发送消息。这个僵局。(SendMessage在消息处理完毕后才返回。)
  3. 与(2)相同,除非对UI线程的窗口使用PostMessage。这起了作用,有一段时间,消息丢失了。(PostMessage立即返回。)进一步的调查显示,消息队列的配额(默认为10,000条)已被超过。
  4. 我增加了消息队列的配额(注册表中的变量NT\CurrentVersion\Windows\USERPostMessageLimit ),但是丢失的消息数量没有变化。
  5. 我让每个工作线程缓冲区消息在4个KByte缓冲区中,当缓冲区填充时使用PostMessage。此操作失败,因为UI线程从未收到任何消息。当我将缓冲区大小增加到64 KBytes时,情况也是如此。

工作线程以“最低”优先级运行,UI线程以“正常”优先级运行。工作线程用代码发送消息,如

代码语言:javascript
复制
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正在用代码接收它们,如

代码语言:javascript
复制
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线程似乎从未在缓冲区中接收消息,即使发送消息的机制与发布单个报告的机制相同?

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

EN

回答 3

Stack Overflow用户

发布于 2013-10-03 00:05:56

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

票数 4
EN

Stack Overflow用户

发布于 2016-08-31 19:52:37

Windows上的MFC工作线程有多个选项可与主线程通信。您有标准的线程信令和同步原语 (互斥、信号量、事件)、易于使用的PostMessage和更高性能的I/O完成港机制。

代码语言:javascript
复制
// 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. 如果UI没有被阻止,合并消息可能会有所帮助。

对你的问题的回答:

  1. 重新评估是否真的需要在突发中发送数千条消息。用户每秒需要多少次更改?如果您需要将它们全部发送,请查看I/O完成端口机制。
  2. 我是不会尝试的,而且可能成功了,除非.
  3. 您的主UI线程被阻塞,等待工作人员在WaitForMultipleObjects中完成,而您的工作人员和其他事件在阻塞时必须生成多个最大队列消息。
票数 2
EN

Stack Overflow用户

发布于 2013-10-03 10:39:34

在GUI上发布进度报告没有多大意义,因为它们可以更快地被用户吸收。当其他线程中有大量活动时,通常使用GUI计时器轮询线程中的进度vars,因此每隔500‘s更新GUI控件一次。

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

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

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/19144112

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档