2011-01-13 15 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