2011-01-13 5 views
0

Я хочу создать многоразовый фоновый поток для очереди на ряд задач, требующих доступа к ресурсу singleton. Поток должен быть создан в начале программы, и сообщение будет отправлено всякий раз, когда требуется выполнить задачу. Сначала я пытался использовать рабочий поток, поскольку фоновый поток не имеет пользовательского интерфейса, но потом я заметил, что только потоки пользовательского интерфейса имеют сообщения насосов. К сожалению, PostThreadMessage всегда возвращает ERROR_INVALID_THREAD_ID, но я уверен, что поток был создан правильно.Многоразовая фоновая нить в MFC

  1. Является ли хорошим выбором использовать поток пользовательского интерфейса, а не рабочий поток?
  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

Смежные вопросы