关于线程的终止问题

    技术2022-05-11  61

    在开发的时候,发现线程没有真正的终止,一直在吃CPU,仔细分析,做了个测试程序,发现问题如下1.从CWinThread派生了自己的CMyThread类,并且,这是一个UI线程(其实这个影响不大,在其他人的文章里也提过,任何线程都能处理消息,只是UI线程会有一个隐藏起来的窗口。)1.1重载InitInstance函数         BOOL CMyThread::InitInstance()         {             TRACE( "CMyThread::InitInstance/n");             this->Run();             return TRUE;         }1.2重载ExitInstance函数         int CMyThread::ExitInstance()         {            TRACE("CMyThread::ExitInstance/n");            ::Sleep(500);            return CWinThread::ExitInstance();         }1.3在Dialog的Button事件里启动线程      m_pThread = ::AfxBeginThread( RUNTIME_CLASS(CMyThread ),THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED );      ASSERT ( m_pThread );

          m_pThread->m_bAutoDelete = FALSE;      m_pThread->ResumeThread();1.4在Stop事件中终止线程      m_pThread->PostThreadMessage( WM_QUIT, NULL, NULL );      ::WaitForSingleObject( m_pThread->m_hThread, INFINITE );

          delete m_pThread;      m_pThread = NULL;      TRACE("______Oh~How hard I reach it/n");

    程序运行的调试信息如下:       CMyThread::InitInstance       CMyThread::ExitInstance

    问题如下:    个人感觉这应该是属于消息循环处理不当的问题,我曾试过不调用Run()进入消息循环(当然也不Post WM_QUIT了),线程是会主动结束的,也就是说,会有以下调试信息输出:            ______Oh~How hard I reach it

        但是如果使用Run()来处理消息循环,就始终得不到该调试信息。其问题应该是由于线程没有真正结束。线程句柄(m_hThread)没有被激发,所以WaitForSingleObject一直处于等待当中。

        于是,真正的问题出来了。我确实向该线程序发送了WM_QUIT消息,并且,Run()也接收到消息,终止了消息循环,进入到ExitInstance函数中了(参看调试输出信息)。这样来说,消息循环应该是终止了才对的。那么,和不调用Run()结束了有区别么,因为已经从Run返回了啊。那么又是什么导致线程没有结束呢?

    ----------------------------------<<可爱的分割线>>---------------------------------------------------------------------------------------

    问题出在对CWinThread的理解不深上~首先,我在InitInstance中调用了Run(),那么跟踪到trdcore.cpp中的代码如1.前面所说的    CMyThread::ExitInstance    这条调试信息并非是在InitInstance返回后调用ExitInstance所输出的信息,而是     if (!PumpMessage())            return ExitInstance();    输出的。2.当CWinThread::Run()返回之后,会跳到    UINT APIENTRY _AfxThreadEntry(void* pParam)中继续执行以下代码         //......省略      if (pThread->m_pfnThreadProc != NULL)      {            nResult = (*pThread->m_pfnThreadProc)(pThread->m_pThreadParams);            ASSERT_VALID(pThread);      }      // else -- check for thread with message loop      else if (!pThread->InitInstance())      {            ASSERT_VALID(pThread);            nResult = pThread->ExitInstance();      }      else      {            // will stop after PostQuitMessage called            ASSERT_VALID(pThread);            nResult = pThread->Run();      }      //......省略    因为我之前在InitInstance中调用了Run(),但是返回的是TRUE,所以,这里会再次进入消息循环,导致线程序无法按照我的意图结束。

    总结:错误残生的原因:    在这之前,我一直把这个InitInstance()的返回值和CDialog::OnInitDialog()的返回值混淆了,CDialog的处理如return TRUE;  // 除非设置了控件的焦点,否则返回 TRUE    但是,CWinThread::InitInstance()的返回值控制着是否进入系统自带的消息循环。

    处理方式:要处理消息循环有两种方式:    1.直接在InitInstance()中返回TRUE,以进入系统为我们准备好的循环    2.如果有需要改写Run()或者自己需要额外的处理,要在InitInstance()中调用Run()进入循环,那么就必须返回FALSE,以避免再次进入系统自带的循环。

    新的问题 CMyThread::ExitInstance()会两次进入一次是在Run()中        if (!PumpMessage())            return ExitInstance();再一次是在下面代码中。      else if (!pThread->InitInstance())      {            ASSERT_VALID(pThread);            nResult = pThread->ExitInstance();      }

    解决方法:处理办法:把自己要处理的事情分配如下:1.初始化工作分配在InitInstance()中2.消息循环和其他的工作分配在自己重载的Run()函数中3.资源释放及处理放在ExitInstance()中

    4.这点是重点,就是不要在InitInstance()中调用Run()进入消息循环,改为在InitInstance()中返回TRUE,由系统来调用我们重载好的Run()函数,走一条清晰的路线!

    (此篇完结),这里给出CWinThread::Run()的代码,里面揭示了缺省的消息处理方式

     

    int CWinThread::Run(){        ASSERT_VALID(this);        _AFX_THREAD_STATE* pState = AfxGetThreadState();

            // for tracking the idle time state        BOOL bIdle = TRUE;        LONG lIdleCount = 0;

            // acquire and dispatch messages until a WM_QUIT message is received.        for (;;)        {            // phase1: check to see if we can do idle work            while (bIdle &&!::PeekMessage(&(pState->m_msgCur), NULL, NULL, NULL, PM_NOREMOVE))            {                // call OnIdle while in bIdle state                if (!OnIdle(lIdleCount++))                    bIdle = FALSE; // assume "no idle" state            }

                // phase2: pump messages while available            do            {                // pump message, but quit on WM_QUIT                if (!PumpMessage())                    return ExitInstance();

                    // reset "no idle" state after pumping "normal" message                //if (IsIdleMessage(&m_msgCur))                if (IsIdleMessage(&(pState->m_msgCur)))                {                    bIdle = TRUE;                    lIdleCount = 0;                }

                } while (::PeekMessage(&(pState->m_msgCur), NULL, NULL, NULL, PM_NOREMOVE));        }}


    最新回复(0)