MFC:如何从主线程使用 MsgWaitForMultipleObjects() 来等待多个线程完成使用 SendMessage()?

Posted

技术标签:

【中文标题】MFC:如何从主线程使用 MsgWaitForMultipleObjects() 来等待多个线程完成使用 SendMessage()?【英文标题】:MFC: How to use MsgWaitForMultipleObjects() from the main thread to wait for multiple threads to complete that use SendMessage()? 【发布时间】:2021-03-17 02:15:37 【问题描述】:

我有一个主线程,它会根据用户从主 UI 中选择的内容触发其他几个线程来完成各种工作。通常我会使用 WaitForMultipleObjects() 并将 bWaitAll 设置为 TRUE。但是,在这种情况下,那些其他线程会将输出记录到另一个窗口,该窗口使用互斥锁来确保线程一次只输出一个。该过程的一部分使用SendMessage() 发送获取文本大小并将文本发送到如果使用WaitForMultipleObjects() 将挂起的窗口,因为它是从主UI 线程运行的。所以我转而使用 MsgWaitForMultipleObjectsQS_SENDMESSAGE 标志,唯一的问题是 bWaitAll 的逻辑,它指出只有在所有对象都发出信号 AND 时才会返回输入事件发生(而不是在所有对象都收到信号输入事件发生时返回)。如果逻辑是OR,这应该可行:

  DWORD waitres=WAIT_FAILED;

  while (1)
  
    MSG msg;
    while (::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) 
      // mfc message pump
      if (!theApp.PumpMessage()) 
        // program end request
        // TO DO
      
    
    // MFC idel processing
    LONG lidlecount = 0;
    while (theApp.OnIdle(lidlecount++));
    // our wait
    waitres = ::MsgWaitForMultipleObjects(threadcount, threadhandles, TRUE, INFINITE, QS_SENDMESSAGE);
    // check if ended due to message
    if (waitres!=WAIT_OBJECT_0+threadcount) 
      // no, exit loop
      break;
    
  

与其触发一个线程然后触发其他线程,我想知道从主线程处理这个问题的正确方法是什么?我考虑过使用 bWaitAll FALSE 然后使用 WaitForMultipleObjects() 并将 bWaitAll 设置为 TRUE 并将 dwMilliseconds 设置为 0(或 1)并检查结果看是否完成。如果没有,它需要循环回到循环的顶部,然后循环到MsgWaitForMultipleObjects(),当使用 bWaitAll FALSE 时,如果多个线程之一完成(比如 10 个线程中的 1 个线程),它可能会立即返回完成,我可以如上所述检查是否全部完成,但是当返回 bWaitAll FALSE 时它只会返回而不是等待)。

那么在 MFC 应用程序的主线程中处理等待多个线程(使用 SendMessage())完成的正确方法是什么?

谢谢。

【问题讨论】:

使用MsgWaitForMultipleObjects()bWaitAll=FALSE 是正确的解决方案。将线程句柄放入数组中,每次等待指示线程完成时,从数组中删除该线程。继续循环直到数组为空。只要等待指示有待处理的消息,就抽出消息队列。 一般来说 - 是的,您永远不必将 bWaitAll 设置为 TRUE。使用bWaitAll=FALSE 并根据需要更改等待数组。万一线程/进程 - 当然在它发出信号后从数组中删除句柄。但对于具体任务 - 所有解决方案都是错误的。你不需要等待线程句柄。您需要为具有引用计数的任务创建通用结构。当线程退出时 - 它释放引用计数。当结构被破坏(不再有线程)向您的窗口发布消息时 【参考方案1】:

那么处理等待多个线程的正确方法是什么? 完成

需要创建一些具有引用计数的结构并将指向该结构的指针传递给每个线程。这里也可能存在意义有一些常见的任务数据。和主(GUI)线程中某些窗口的HWND。当工作线程退出时 - 它释放对对象的引用。当最后一个线程退出时 - 删除对象并从主线程向窗口发布一些消息。

所以我们不需要存储线程句柄(可以直接关闭它)并等待多个句柄。相反,当所有线程完成任务时,我们会收到一些窗口消息

代码示例

struct Task 

    HWND _hwnd;
    LONG _dwRefCount = 1;
    // some common task data probably ..

    Task(HWND hwnd) : _hwnd(hwnd) 

    ~Task() 
        PostMessageW(_hwnd, WM_USER, 0, 0);// WM_USER as demo only
    

    void AddRef()
        InterlockedIncrementNoFence(&_dwRefCount);
    

    void Release()
        if (!InterlockedDecrement(&_dwRefCount)) delete this;
    
;

ULONG CALLBACK WorkThread(void* pTask)

    WCHAR sz[16];
    swprintf_s(sz, _countof(sz), L"%x", GetCurrentThreadId());
    MessageBoxW(0, L"working...", sz, MB_ICONINFORMATION|MB_OK);
    reinterpret_cast<Task*>(pTask)->Release();
    return 0;


void StartTask(HWND hwnd, ULONG n)

    if (Task* pTask = new Task(hwnd))
    
        do 
        
            pTask->AddRef();

            if (HANDLE hThread = CreateThread(0, 0, WorkThread, pTask, 0, 0))
            
                CloseHandle(hThread);
            
            else
            
                pTask->Release();
            
         while (--n);

        pTask->Release();
    

【讨论】:

以上是关于MFC:如何从主线程使用 MsgWaitForMultipleObjects() 来等待多个线程完成使用 SendMessage()?的主要内容,如果未能解决你的问题,请参考以下文章

如何从主线程超时java线程?

如何从主线程终止子线程[重复]

从主函数向线程发送信号?

mfc如何使用多线程搜索文件夹?

从主线程中删除 AlamofireImage responseImage

如何使用 CWinThread 在 MFC 中创建工作线程?