我们如何安全地只启动一个WorkerThread?
有这样的设计模式吗?
其实我就是这样做的,似乎有点过于复杂了。
void CLogView::OnStartStoplogger()
{
m_bLogPtRun = !m_bLogPtRun; // (bool)
if (m_bLogPtRun)
{
// First check, if Thread is still running
if (!m_pLogPointThread)
{
m_pLogPointThread = AfxBeginThread(AddLogPointThread, this);
}
else
return; // Thread still running
}
else
{
if (m_pLogPointThread )
{
if (m_pLogPointThread) {
DWORD dwRET = WaitForSingleObject(m_pLogPointThread->m_hThread, /*INFINITE*/ 5000);
if (dwRET != WAIT_OBJECT_0)
MessageBox(_T("Could not finished LogPointThread"));
}
}
好的,下面是一个示例,假设您创建了一个工作线程,它将一直运行到应用程序退出为止。用户可以暂停和恢复数据检索和记录。正如注释中所述,不需要为此使用 APC,而是使用同步来控制工作线程。我建议的同步对象是手动重置事件。它充当“同步布尔值”,由主 (UI) 线程设置和清除 - 如果工作线程未发出信号,则工作线程会等待它。
1。变量和初始化
BOOL bInitOK = FALSE; // Worker Thread Successfully Initialized
BOOL bChkRTD = FALSE; // Retrieval & Logging of RTD Enabled
HANDLE m_hEvt = CreateEvent(NULL, TRUE, FALSE, NULL); // Controlling Retrieval & Logging of RTD
CWinThread *m_pLogPointThread = AfxBeginThread(AddLogPointFN, this); // Worker Thread - Created once
请注意,这里的
AddLogPointFN
是一个AFX_THREADPROC
函数,而不是一个类。这将创建一个没有消息队列的线程。此外,您不需要定义和使用任何线程本地数据(即每个线程实例化的数据),因为线程仅实例化一次,即您的数据本质上是“全局”的(尽管它们可能包含在另一个单实例中)类,如主窗口或视图)。或者,您可以使用 CreateThread()
函数和 LPTHREAD_START_ROUTINE
创建线程 - 这个是 _stdcall
。该事件控制工作线程是否寻找数据;它有点“复制”由用户设置的 bChkRTD
变量 - 事件是可等待对象,而布尔变量则不是。
2。线程程序
UINT AddLogPointFN(LPVOID lParam)
{
// CLogView* variable - For convenience
CLogView* pView = (CLogView*)lParam; // Assuming lParam is CLogView
// Add one-time, resource-heavy initialization here, eg connections
.
.
bool bInit = InitConnections();
if (!bInit)
{
// Initialization Failed - Tell UI to display an error-message or exit
pView->PostMessage(USERMESSAGE_ALPNOTIFY, 1);
return 1;
}
// Initialization Successful - Tell UI to enable RTD request menus or buttons
pView->PostMessage(USERMESSAGE_ALPNOTIFY, 2);
// Wait if m_hEvt is non-signaled
while(WaitForSingleObject(pView->m_hEvt,INFINITE)==WAIT_OBJECT_0)
{
// Retrieve RTD
.
.
// Data retrieved, send them to the UI thread (for display, logging etc)
MY_DATA *p_Data = (MY_DATA*)GlobalAlloc(GPTR, sizeof(MY_DATA));
GetMyData(p_Data);
pView->PostMessage(USERMESSAGE_ALPNOTIFY, 3, (LPARAM)p_Data);
}
return 0;
}
RTD 检索是通过等待事件对象来控制的。该事件是手动重置的,即等待函数不会重置它 - 它仅由 UI 线程设置/重置。线程不会直接修改(全局)
bInitOK
变量(这需要同步访问),而是通知 UI 线程进行设置。
3.主线程项目和例程
// Start/Stop Command and UI Update
void CLogView::OnStartStoplogger()
{
bChkRTD = !bChkRTD; // Check-box behavior, see also ON_UPDATE_COMMAND_UI
if (bChkRTD) SetEvent(m_hEvt);
else ResetEvent(m_hEvt);
}
void CLogView::OnUpdateStartStoplogger(CCmdUI *pCmdUI)
{
pCmdUI->Enable(bInitOK); // Enabled if Initialization Successful
pCmdUI->SetCheck(bChkRTD); // Checked if RTD Retrieval Enabled
}
// USERMESSAGE_ALPNOTIFY Handler
afx_msg LRESULT CLogView::OnALPNotify(WPARAM wParam, LPARAM lParam)
{
switch(wParam)
{
case 1: // Initialization Failed - Show some message or exit
bInitOK = FALSE;
.
.
break;
case 2: // Initialization Successful - Enable Start/Stop UI item
bInitOK = TRUE;
break;
case 3: // New RTD Available - Display the data
// E.g. copy the data returned from the worker thread to some local structure
CopyMyData(m_pMyData, (MY_DATA*)lParam);
GlobalFree((HGLOBAL)lParam)
UpdateData(FALSE);
break;
}
}