2011-01-13 14 views
0

我想創建一個可重用的後臺線程來排隊一些需要訪問單例資源的任務。線程應該在程序開始時創建,並且只要需要完成任務就會發送一條消息。起初我試圖使用工作線程,因爲後臺線程沒有UI,但後來我注意到只有UI線程有消息泵。不幸的是,PostThreadMessage始終返回ERROR_INVALID_THREAD_ID,但我確定該線程已正確創建。在MFC中可重複使用的後臺線程

  1. 使用UI線程而不是工作線程是不錯的選擇嗎?
  2. 爲什麼我的PostThreadMessage沒有收到?

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

當你的線程初始化它只需要調用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; 
    } 


}; 
+0

問題是我的子類`InitInstance`函數返回與超類相同的值。 `CWinThread :: InitInstance`返回`FALSE`,這被解釋爲初始化失敗 – Casebash 2011-02-14 23:10:40