簡體   English   中英

工作線程如何與主UI線程通信?

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

工作線程與主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),但丟失的消息數沒有改變。

  5. 我將每個工作線程緩沖消息放在4 KB緩沖區中,並在緩沖區填充時使用PostMessage。 這失敗了,因為UI線程從未收到任何消息。 當我將緩沖區大小增加到64 KB時也是如此。

工作線程以“最低”優先級運行,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線程似乎永遠不會收到緩沖區中的消息,即使傳輸它們的機制與發布單個報告相同?

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

使用WaitForMultipleObjects調用中的主線程,不會處理任何消息,也不會更新任何控件或其他窗口。 解決方案是:不要那樣做。

在GUI上發布進度報告沒有太多意義,比用戶可以同意的更快。 當其他線程中存在大量活動時,通常使用GUI計時器輪詢線程中的進度變量,因此每次更新GUI控件,比如500ms。

這是定時器輪詢實際上有利的極少數時間之一。 您可以獲得peridoc進度報告,而不會有更新GUI GUI消息隊列的危險。 例如,uTorrent客戶端(其中存在大量網絡活動)使用此方案 - 嘗試更新收到的每個網絡協議單元上的GUI下載統計信息肯定會填充GUI。

(5)中的緩沖方案應該有效。 我經常通過將對象指針加載到LPARAM或WPARAM中來將大數據項傳輸到主GUI線程,在工作線程中新建它們並在顯示后在GUI中刪除它們。 你的(5)應該有效,至少減少了進度數據傳輸的開銷。 我只能假設要顯示的數據量仍然太大,因此GUI線程仍然無法跟上:(

Windows上的MFC工作線程有多個與主線程通信的選項。 您有標准的線程信令和同步原語 (互斥,信號量, 事件 ),易於使用的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,...);

如果您阻止或忙碌等待線程完成,它們都不會完成太多工作或提高您的性能或響應能力。

評論你的意見:

  1. 不要讓工人觸摸GUI。
  2. 不要從工作線程使用SendMessage。
  3. 只要UI可以保持並且不被阻止,PostMessage適用於低容量。
  4. 如果您認為這需要改變,您應該重新考慮您的解決方案。 發送更少或使用更高性能的選項。
  5. 如果未阻止UI,則合並消息可能有所幫助。

您的問題的答案:

  1. 如果你真的需要在一個爆發中發送數千條消息,請重新評估。 用戶需要每秒多少次更改? 如果需要全部發送,請查看I / O完成端口機制。
  2. 我不會嘗試,它可能有效,除了...
  3. 您的主UI線程被阻塞,等待工作人員在WaitForMultipleObjects中完成,而您的工作人員和其他被阻止的事件必須生成超過最大隊列消息。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM