MFC中可重复使用的后台线程

时间:2011-01-13 06:02:23

标签: c++ mfc

我想创建一个可重用的后台线程来排队需要访问单例资源的许多任务。应该在程序开始时创建线程,并且只要需要完成任务就会发送消息。起初我试图使用工作线程,因为后台线程没有UI,但后来我注意到只有UI线程有消息泵。不幸的是,PostThreadMessage始终返回ERROR_INVALID_THREAD_ID,但我确信线程已正确创建。

  1. 使用UI线程而不是工作线程是一个不错的选择吗?
  2. 为什么我的PostThreadMessage没有收到?
  3. UPDATE :通过查看输出消息,我现在知道因为线程被杀死而没有收到消息

    示例代码

    DWORD deviceControllerThread;
    
    void post(){
        BOOL res=PostThreadMessage(deviceControllerthread,ControllerThread, ENROLLMENT_BEGIN, (WPARAM) myDataPointer, 0);
        ...
    }
    
    void MFC_Init(){
        CWinThread* thread=AfxBeginThread(RUNTIME_CLASS(MFC_thread), THREAD_PRIORITY_NORMAL, 0, 0);
        deviceControllerThread=thread->m_nThreadID;
    }
    

1 个答案:

答案 0 :(得分:1)

当你的线程初始化时,只需要调用PeekMessage来“创建”一个消息队列。然后另一个线程可以通过PostThreadMessage向它发布消息。此外,INVALID_THREAD_ID的错误代码是您的工作线程实际已退出(或从未创建)的症状。确保您有足够的调试速度或日志记录来验证工作线程是否已创建且未过早退出。另外,请确保检查AfxBeginThread的返回码并且m_nThreadID有效(因为我假设您将其初始化为零)。

我一直在进行类似的线程练习。我已经不再使用消息队列了,而是使用我自己的事件和队列来进行更好的控制。

如果您不需要保证工作项的订购,那么另一个想法是只使用Windows“线程池”为您完成工作。

下面是我通常如何在C ++中构造线程类的概述。这只是我根据现有项目鞭打的东西,而不是生产代码。但它应该展示一些如何管理线程生命周期的概念。

// CList is any generic "array" or "list" class (you can use std::list, CAtlArray, CSimpleArray, etc...)

// ThreadMessage is your data structure for holding data to indicate to the thread what to do
// e.g.
// struct ThreadMessage
//{
//    enum type; // YOUR_CODE_TO_QUIT=0, WORK_MESSAGE=1, etc...
//    workdata data;
//};

class CMyThread
{
private:
     CRITICAL_SECTION m_cs;  // lock such that m_queue is thread safe, can be replaced with CComAutoCriticalSection or equivalent
     bool m_fNeedToExit;     // signals to the worker thread that it is time to exit
     HANDLE m_hEvent;        // For waking up the worker thread to tell it a new message is available
     HANDLE m_hThread;       // handle to worker thread
     HANDLE m_hStartEvent;   // For the worker thread to signal back to the parent thread that is has finished initializing
     bool m_fStarted;        // Has Start() been called
     DWORD m_dwThread;       // threadID
     CList<ThreadMessage> m_queue; // generic "array" of work items.  Can be replaced with any list-type data structure

public:
     CMyThread()
     {
          InitializeCriticalSection(&m_cs);
     }

     ~CMyThread()
     {
         Stop();
         DeleteCriticalSection(&m_cs);
     }

     HRESULT Start()
     {
         if (m_fStarted)
            return S_FALSE;

         // todo - check all return codes from the Create functions!

         m_hEvent = CreateEvent(0,0,0,0);  // unsignalled, unnamed, auto-reset event
         m_hStartEvent = CreateEvent(0,0,0,0);  // unsignalled, unnamed, auto-reset event
         m_hThread = CreateThread(NULL, 0, CMyThread::ThreadProc, this, 0, &m_dwThreadID);

         // PUT YOUR THREAD INITIALIZATION CODE HERE


         // wait for the thread to intialize (you don't have to call this next line if the thread doesn't have any initialization to wait for */
         WaitForSingleObject(m_hStartEvent, INFINITE);

         m_fStarted = true;

         return S_OK;

     }

     HRESULT Stop()
     {

         if (m_hThread)
         {
             m_fNeedToExit = true;
             ThreadMessage quitmessage;
             quitmessage.type = YOUR_CODE_TO_QUIT;
             SendMessageToThread(&quitmessage);

             // in a debug build, you may want to wait for X seconds and show an error message if the worker thread appears hung

             WaitForSingleObject(m_hThread, INFINITE);

             // cleanup
             CloseHandle(m_hThread); m_hThread = NULL;
             CloseHandle(m_hStartEvent); m_hStartEvent = NULL;
             CloseHandle(m_hEvent); m_hEvent= NULL;
             m_fStarted = true;
             m_dwThread = 0;
             m_queue.empty();
         }

         return S_OK;
     }

     HRESULT SendMessageToThread(Message* pMsg)
     {
         if (m_fStarted == false)
             return E_FAIL;

         EnterCriticalSection(&m_cs);
             m_queue.enque(*pMsg); //push message onto queue
         LeaveCriticalSection(&m_cs);

         SetEvent(m_hEvent); // signal the thread to wakeup and process it's message queue

         return S_OK;


     }


     void ThreadProcImpl()
     {

         // initialize thread if needed (e.g. call PeekMessage to initialize the message queue if you need one - in this implementation you don't)
         // signal back to the main thread we're off and running
         SetEvent(m_hThreadStarted);

         while (m_fNeedToExit == false)
         {
             bool fGotMsg = false;
             ThreadMessage msg;

             EnterCriticalSection(&m_cs);
                 if (m_queue.size > 0)
                 {
                     msg = m_queue.deque(); // remove the first message from the queue (if any)
                     fGotMsg = true;
                 }
             LeaveCriticalSection(&m_cs);

             // if the queue is empty, then wait for another message to come in
             if (fGotMsg == false)
             {
                 WaitForSingleObject(m_hEvent, INFINITE); // on return m_hEvent is auto-reset to unsignalled
                 continue; // back to top of while loop to deque
             }

             if (m_fNeedToExit)  // check exit condition
                 break;

             if (msg.type == YOUR_CODE_TO_QUIT)
                 break;


             // YOUR CODE TO HANDLE "ThreadMessage msg" goes here. (i.e. "do the work")

         }

         // thread cleanup code goes here (if any)
     }


     static DWORD __stdcall ThreadProc(void* pcontext)
     {
        CMyThread* pThis = (CMyThread*)pcontext;
        pThis->ThreadProcImpl();
        return 0;
     }


};