Многоразовый фоновый поток в MFC - PullRequest
0 голосов
/ 13 января 2011

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

  1. Является ли правильным выбором использовать поток пользовательского интерфейса, а не рабочий поток?
  2. Почему мои PostThreadMessage не принимаются?

ОБНОВЛЕНИЕ : просматривая выходные сообщения, я теперь знаю, что сообщение не получено, поскольку поток уничтожен

Пример кода

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 Ответ

1 голос
/ 13 января 2011

Когда ваш поток инициализируется, ему просто нужно вызвать 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;
     }


};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...