只允许启动一个线程

问题描述 投票:0回答:1

我们如何安全地只启动一个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"));
      }
}
     
c++ mfc
1个回答
1
投票

好的,下面是一个示例,假设您创建了一个工作线程,它将一直运行到应用程序退出为止。用户可以暂停和恢复数据检索和记录。正如注释中所述,不需要为此使用 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;
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.