简体   繁体   English

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

[英]How can worker threads communicate with main UI thread?

What is the best way for worker threads to communicate with the main UI thread? 工作线程与主UI线程通信的最佳方式是什么?

Summary: My C++/MFC application is dialog-based. 简介:我的C ++ / MFC应用程序是基于对话框的。 To do lengthy computations, the main UI thread creates several worker threads. 要进行冗长的计算,主UI线程会创建多个工作线程。 As the worker threads progress in the calculation, they report their progress to the main UI thread, which then displays progress. 当工作线程在计算中进展时,它们会将进度报告给主UI线程,然后显示进度。

This works fine for numeric progress values, which are in shared memory (written by workers, read by UI), but I'm having trouble with text progress messages. 这适用于数字进度值,它们位于共享内存中(由工作人员编写,由UI读取),但我遇到了文本进度消息的问题。 My attempted solutions have been through several iterations, and none seems to work. 我尝试过的解决方案已经过了几次迭代,但似乎都没有。

  1. I had the UI thread pass pointers to controls to the workers, and the workers updated the UI directly. 我让UI线程传递指向工作者控件的指针,工作人员直接更新了UI。 This wasn't very effective, and seems like the wrong approach. 这不是很有效,似乎是错误的方法。

  2. I had the workers send messages, using SendMessage to the UI thread's window. 我让工作人员使用SendMessage向UI线程的窗口发送消息。 This deadlocked. 这陷入僵局。 (SendMessage doesn't return until the message has been processed.) (在处理完消息之前,SendMessage不会返回。)

  3. Same as (2), except using PostMessage to the UI thread's window. 与(2)相同,除了将PostMessage用于UI线程的窗口。 This worked, for a while, then messages got lost. 这工作了一段时间,然后消息丢失了。 (PostMessage returns immediately.) Further investigation revealed the quota for message queues, which defaults to 10,000, was being exceeded. (PostMessage立即返回。)进一步调查显示,超出了消息队列的配额,默认为10,000。

  4. I increased the quota for message queues (variable HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows\\USERPostMessageLimit in the registry), but the number of lost messages didn't change. 我增加了消息队列的配额(注册表中的变量HKEY_LOCAL_MACHINE \\ SOFTWARE \\ Microsoft \\ Windows NT \\ CurrentVersion \\ Windows \\ USERPostMessageLimit),但丢失的消息数没有改变。

  5. I had each worker thread buffer messages in 4 KByte buffers, and PostMessage when a buffer filled. 我将每个工作线程缓冲消息放在4 KB缓冲区中,并在缓冲区填充时使用PostMessage。 This failed because the UI thread never received any messages. 这失败了,因为UI线程从未收到任何消息。 Same was true when I increased the buffer size to 64 KBytes. 当我将缓冲区大小增加到64 KB时也是如此。

The worker threads are running at "lowest" priority, and the UI thread at "normal" priority. 工作线程以“最低”优先级运行,UI线程以“正常”优先级运行。 Worker threads are sending messages with code like 工作线程正在发送带有代码的消息

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*/);

and UI is receiving them with code like 和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;
}

Questions: 问题:

  1. What is the preferred way for workers to issue progress reports, in a case where there may be bursts of several thousand text reports? 在可能爆发数千份文本报告的情况下,工人发布进度报告的首选方式是什么?

  2. Why can I not increase the quota of post messages in a queue? 为什么我不能增加队列中的帖子消息的配额?

  3. Why does the main UI thread seemingly never receive the messages in the buffers, even though the mechanism for transmitting them was identical to posting the individual reports? 为什么主UI线程似乎永远不会收到缓冲区中的消息,即使传输它们的机制与发布单个报告相同?

64-bit Windows 7, Visual Studio 2010, native C++/MFC 64位Windows 7,Visual Studio 2010,本机C ++ / MFC

With the main thread in a WaitForMultipleObjects call no messages will be processed and no controls or other windows can be updated. 使用WaitForMultipleObjects调用中的主线程,不会处理任何消息,也不会更新任何控件或其他窗口。 The solution is: Don't do that. 解决方案是:不要那样做。

There is not much point in issuing progress reports on the GUI any faster than they can be assimilated by the user. 在GUI上发布进度报告没有太多意义,比用户可以同意的更快。 When there is a very large amount of activity in the other threads, it's common to poll the progress vars in the threads using a GUI timer and so update the GUI controls every, say 500ms. 当其他线程中存在大量活动时,通常使用GUI计时器轮询线程中的进度变量,因此每次更新GUI控件,比如500ms。

This is one of those very few times when timer-polling is actually advantageous. 这是定时器轮询实际上有利的极少数时间之一。 You get peridoc progress reports without the danger of stuffing the GUI Windows message queue with updates. 您可以获得peridoc进度报告,而不会有更新GUI GUI消息队列的危险。 For example, uTorrent client, (where there is a huge amount of network activity), uses this scheme - trying to update the GUI download stats on every network protocol unit received would surely stuff the GUI. 例如,uTorrent客户端(其中存在大量网络活动)使用此方案 - 尝试更新收到的每个网络协议单元上的GUI下载统计信息肯定会填充GUI。

You buffering scheme in (5) should have worked. (5)中的缓冲方案应该有效。 I frequently transfer large data items to the main GUI thread by loading an object pointer into LPARAM or WPARAM, new'ing them in the work thread and delete'ing them in the GUI after display. 我经常通过将对象指针加载到LPARAM或WPARAM中来将大数据项传输到主GUI线程,在工作线程中新建它们并在显示后在GUI中删除它们。 Your (5) should have worked and, at least reduced the overhead of progress data transfer. 你的(5)应该有效,至少减少了进度数据传输的开销。 I can only assume that the amount of data to be displayed was still too large and so the GUI thread could still not keep up :( 我只能假设要显示的数据量仍然太大,因此GUI线程仍然无法跟上:(

MFC worker threads on Windows have multiple options for communicating with the main thread. Windows上的MFC工作线程有多个与主线程通信的选项。 You have the standard thread signaling and synchronization primitives (mutex, semaphore, event ), the easy to use PostMessage , and the higher performance I/O Completion Port mechanism. 您有标准的线程信令和同步原语 (互斥,信号量, 事件 ),易于使用的PostMessage ,以及更高性能的I / O完成端口机制。

// 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,...);

None of them will get much done or increase your performance or responsiveness if you block or busy-wait for thread completion. 如果您阻止或忙碌等待线程完成,它们都不会完成太多工作或提高您的性能或响应能力。

Comments on your observations: 评论你的意见:

  1. Don't have the workers touch the GUI. 不要让工人触摸GUI。
  2. Don't use SendMessage from worker threads. 不要从工作线程使用SendMessage。
  3. PostMessage is fine for low volumes as long as UI can keep up and isn't blocked. 只要UI可以保持并且不被阻止,PostMessage适用于低容量。
  4. If you think this needs to change, you should rethink your solution. 如果您认为这需要改变,您应该重新考虑您的解决方案。 Send less or use a more performant option. 发送更少或使用更高性能的选项。
  5. Coalescing messages may have helped, if the UI wasn't blocked. 如果未阻止UI,则合并消息可能有所帮助。

Answers to your questions: 您的问题的答案:

  1. Reevaluate if you really need to send thousands of messages in a burst. 如果你真的需要在一个爆发中发送数千条消息,请重新评估。 How many changes per second does the user need? 用户需要每秒多少次更改? If you need to send them all, look into the I/O Completion Port mechanism. 如果需要全部发送,请查看I / O完成端口机制。
  2. I wouldn't have tried and it may have worked except... 我不会尝试,它可能有效,除了...
  3. Your main UI thread was blocked waiting for the workers to complete in WaitForMultipleObjects and your workers and other events while blocked must have generated more than max queue messages. 您的主UI线程被阻塞,等待工作人员在WaitForMultipleObjects中完成,而您的工作人员和其他被阻止的事件必须生成超过最大队列消息。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM