gpt4 book ai didi

c++ - MFC中可重用的后台线程

转载 作者:行者123 更新时间:2023-11-28 08:24:05 28 4
gpt4 key购买 nike

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

  1. 使用 UI 线程而不是工作线程是一个好的选择吗?
  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;
}

最佳答案

当您的线程初始化时,它只需要调用 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;
}


};

关于c++ - MFC中可重用的后台线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4677256/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com