C++ MFC中可重用的后台线程
我想创建一个可重用的后台线程,对需要访问单例资源的大量任务进行排队。线程应该在程序开始时创建,并在需要完成任务时发送消息。起初我尝试使用工作线程,因为后台线程没有UI,但后来我注意到只有UI线程有消息泵。不幸的是,C++ MFC中可重用的后台线程,c++,mfc,C++,Mfc,我想创建一个可重用的后台线程,对需要访问单例资源的大量任务进行排队。线程应该在程序开始时创建,并在需要完成任务时发送消息。起初我尝试使用工作线程,因为后台线程没有UI,但后来我注意到只有UI线程有消息泵。不幸的是,PostThreadMessage总是返回ERROR\u INVALID\u THREAD\u ID,但我确信线程已正确创建 使用UI线程而不是工作线程是一个好的选择吗 为什么没有收到我的PostThreadMessage 更新:通过查看输出消息,我现在知道由于线程被终止,消息没有被接
PostThreadMessage
总是返回ERROR\u INVALID\u THREAD\u ID
,但我确信线程已正确创建
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向其发布消息。另外,无效线程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;
}
};
//CList是任何通用的“array”或“list”类(可以使用std::list、CAtlArray、CSimpleArray等)
//ThreadMessage是用于保存数据的数据结构,用于向线程指示要执行的操作
//例如。
//结构线程消息
//{
//枚举类型;//您的\u-CODE\u-TO\u-QUIT=0,WORK\u-MESSAGE=1,等等。。。
//工作数据;
//};
类CMyThread
{
私人:
CRITICAL_SECTION m_cs;//锁,使m_队列是线程安全的,可替换为CComAutoCriticalSection或等效项
bool m_fNeedToExit;//向工作线程发出退出时间的信号
HANDLE m_hEvent;//用于唤醒工作线程,告诉它新消息可用
HANDLE m_hThread;//工作线程的句柄
HANDLE m_hStartEvent;//工作线程向完成初始化的父线程发回信号
bool m_fStarted;//是否已调用Start()
DWORD m_dwThread;//threadID
CList m_queue;//工作项的通用“数组”。可以替换为任何列表类型的数据结构
公众:
CMyThread()
{
初始化关键部分(&m_cs);
}
~CMyThread()
{
停止();
删除关键部分(&m_cs);
}
HRESULT Start()
{
如果(已启动)
返回S_FALSE;
//todo-检查创建函数中的所有返回代码!
m_hEvent=CreateEvent(0,0,0,0);//未调用、未命名、自动重置事件
m_hStartEvent=CreateEvent(0,0,0,0);//未调用、未命名、自动重置事件
m_hThread=CreateThread(NULL,0,CMyThread::ThreadProc,this,0,&m_dwThreadID);
//将线程初始化代码放在这里
//等待线程初始化(如果线程没有任何要等待的初始化,则不必调用下一行)*/
WaitForSingleObject(m_hStartEvent,无限);
m_fStarted=真;
返回S_OK;
}
HRESULT Stop()
{
if(m_hThread)
{
m_fNeedToExit=真;
线程消息;
quitmessage.type=要退出的代码;
SendMessageToThread(&quitmessage);
//在调试生成中,如果工作线程出现挂起,可能需要等待X秒并显示错误消息
WaitForSingleObject(m_hThread,无限);
//清理
CloseHandle(m_hThread);m_hThread=NULL;
CloseHandle(m_hStartEvent);m_hStartEvent=NULL;
CloseHandle(m_hEvent);m_hEvent=NULL;
m_fStarted=真;
m_dwThread=0;
m_queue.empty();
}
返回S_OK;
}
HRESULT SendMessageToThread(消息*pMsg)
{
如果(m_fStarted==false)
返回E_失败;
肠危重科(&m_-cs);
m_queue.enque(*pMsg);//将消息推送到队列上
离开关键部门(&m_-cs);
SetEvent(m_hEvent);//通知线程唤醒并处理其消息队列
返回S_OK;
}
void ThreadProcImpl()
{
//如果需要,初始化线程(例如,如果需要,调用PeekMessage初始化消息队列-在这个实现中,您不需要)
//向主线程发回信号,我们开始运行
SetEvent(m_hThreadStarted);
while(m_fNeedToExit==false)
{
bool fGotMsg=false;
线程消息消息;
肠危重科(&m_-cs);
如果(m_queue.size>0)
{
msg=m_queue.deque();//从队列中删除第一条消息(如果有)
fGotMsg=真;
}
离开关键部门(&m_-cs);
//如果队列为空,则等待另一条消息传入
如果(fGotMsg==false)
{
WaitForSingleObject(m_hEvent,无限);//返回m_hEv时