UI线程、工作者线程

转载地址:http://www.cnblogs.com/carekee/articles/3160209.html

一、“UI线程”语源

      据考证,“UI线程”的概念最早可能是在MFC中被引入的。目前能找到的官方提法是在:

      http://msdn.microsoft.com/en-us/library/b807sta6(v=vs.110).aspx

      MFC的AfxBeginThread提供了两个版本:

CWinThread* AFXAPI AfxBeginThread(AFX_THREADPROC pfnThreadProc,

                                    LPVOID pParam,

                                    int nPriority = THREAD_PRIORITY_NORMAL,

                                    UINT nStackSize = 0,

                                    DWORD dwCreateFlags = 0,

                                    LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL);

 

CWinThread* AFXAPI AfxBeginThread(CRuntimeClass* pThreadClass,

                                    int nPriority = THREAD_PRIORITY_NORMAL,

                                    UINT nStackSize = 0,

                                    DWORD dwCreateFlags = 0,

                                    LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL);

      第一个版本用来让人创建“工作者线程”,第二个版本让人用来创建“UI线程”。可能由于来自MFC的远古光环,让“UI线程”的提法略有普及。但除此之外,在Windows开发方面,似乎找不到第二个例子了。不管怎样,既然MFC官方文档里说了,那么在“MFC领域”使用“UI线程”的提法总是可以的。下面,我们先来认识一下MFC中的UI线程以及工作线程。

二、MFC中的UI线程

      我们按照http://msdn.microsoft.com/en-us/library/b807sta6(v=vs.110).aspx的指示,来创建一个“UI线程”。首先,继承CWinThread:

class CMyThread : public CWinThread
{
    DECLARE_DYNCREATE(CMyThread) 

public:
    virtual BOOL InitInstance()
    {
        return TRUE;
    }
};

IMPLEMENT_DYNCREATE(CMyThread, CWinThread)
      然后,随便找个地方来启动线程:
AfxBeginThread(RUNTIME_CLASS(CMyThread));
      线程被创建后,就处于 CWinThread::Run里的消息循环之中了。来看看CWinThread::Run 的实现:
// main running routine until thread exits  
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));  
    }  
} 

      粗粗看一下,Run函数是个夹杂了OnIdle概念的消息循环。

      再看一下AfxBeginThread:

CWinThread* AFXAPI AfxBeginThread(CRuntimeClass* pThreadClass,
    int nPriority, UINT nStackSize, DWORD dwCreateFlags,
    LPSECURITY_ATTRIBUTES lpSecurityAttrs)
{
#ifndef_MT
    pThreadClass;
    nPriority;
    nStackSize;
    dwCreateFlags;
    lpSecurityAttrs;

    return NULL;
#else
    ASSERT(pThreadClass != NULL);
    ASSERT(pThreadClass->IsDerivedFrom(RUNTIME_CLASS(CWinThread)));

    CWinThread* pThread = (CWinThread*)pThreadClass->CreateObject();
    if (pThread == NULL)
        AfxThrowMemoryException();
    ASSERT_VALID(pThread);

    pThread->m_pThreadParams = NULL;
    if (!pThread->CreateThread(dwCreateFlags|CREATE_SUSPENDED, nStackSize,
        lpSecurityAttrs))
    {
        pThread->Delete();
        return NULL;
    }
    VERIFY(pThread->SetThreadPriority(nPriority));
    if (!(dwCreateFlags & CREATE_SUSPENDED))
    {
        ENSURE(pThread->ResumeThread() != (DWORD)-1);
    }
    return pThread;
#endif//!_MT
}
      其中调用了 CWinThread::CreateThread
BOOL CWinThread::CreateThread(DWORD dwCreateFlags, UINT nStackSize,
    LPSECURITY_ATTRIBUTES lpSecurityAttrs)
{
#ifndef_MT
    dwCreateFlags;
    nStackSize;
    lpSecurityAttrs;

    return FALSE;
#else
    ENSURE(m_hThread == NULL);  // already created?
 
    // setup startup structure for thread initialization
    _AFX_THREAD_STARTUP startup; memset(&startup, 0, sizeof(startup));
    startup.pThreadState = AfxGetThreadState();
    startup.pThread = this;
    startup.hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
    startup.hEvent2 = ::CreateEvent(NULL, TRUE, FALSE, NULL);
    startup.dwCreateFlags = dwCreateFlags;

    if (startup.hEvent == NULL || startup.hEvent2 == NULL)
    {
        TRACE(traceAppMsg, 0, "Warning: CreateEvent failed in CWinThread::CreateThread.\n");
        if (startup.hEvent != NULL)
             ::CloseHandle(startup.hEvent);
        if (startup.hEvent2 != NULL)
             ::CloseHandle(startup.hEvent2);
        return FALSE;
    }

    // create the thread (it may or may not start to run)
    m_hThread = (HANDLE)(ULONG_PTR)_beginthreadex(lpSecurityAttrs, nStackSize, 
        &_AfxThreadEntry, &startup, dwCreateFlags | CREATE_SUSPENDED, (UINT*)&m_nThreadID);
    if (m_hThread == NULL)
    {
        ::CloseHandle(startup.hEvent);
        ::CloseHandle(startup.hEvent2);
        return FALSE;
    }

    // start the thread just for MFC initialization
    VERIFY(ResumeThread() != (DWORD)-1);
    VERIFY(::WaitForSingleObject(startup.hEvent, INFINITE) == WAIT_OBJECT_0);
    ::CloseHandle(startup.hEvent); 

    // if created suspended, suspend it until resume thread wakes it up
    if (dwCreateFlags & CREATE_SUSPENDED)
        VERIFY(::SuspendThread(m_hThread) != (DWORD)-1);

    // if error during startup, shut things down
    if (startup.bError)
    {
        VERIFY(::WaitForSingleObject(m_hThread, INFINITE) == WAIT_OBJECT_0);
        ::CloseHandle(m_hThread);
        m_hThread = NULL;
        ::CloseHandle(startup.hEvent2);
        return FALSE;
    }

    // allow thread to continue, once resumed (it may already be resumed)
    VERIFY(::SetEvent(startup.hEvent2));
    return TRUE;
#endif//!_MT
}
      线程函数为 _AfxThreadEntry
UINT APIENTRY _AfxThreadEntry(void* pParam)
{
    _AFX_THREAD_STARTUP* pStartup = (_AFX_THREAD_STARTUP*)pParam;
    ASSERT(pStartup != NULL);
    ASSERT(pStartup->pThreadState != NULL);
    ASSERT(pStartup->pThread != NULL);
    ASSERT(pStartup->hEvent != NULL);
    ASSERT(!pStartup->bError);

    CWinThread* pThread = pStartup->pThread;
    CWnd threadWnd;
    TRY
    {
        // inherit parent's module state
        _AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
        pThreadState->m_pModuleState = pStartup->pThreadState->m_pModuleState;

        // set current thread pointer for AfxGetThread
        AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
        pThread->m_pModuleState = pModuleState;
        AFX_MODULE_THREAD_STATE* pState = pModuleState->m_thread;
        pState->m_pCurrentWinThread = pThread;

        // forced initialization of the thread
        AfxInitThread();

        // thread inherits app's main window if not already set
        CWinApp* pApp = AfxGetApp();
        if (pApp != NULL &&
             pThread->m_pMainWnd == NULL && pApp->m_pMainWnd->GetSafeHwnd() != NULL)
        {
             // just attach the HWND
             threadWnd.Attach(pApp->m_pMainWnd->m_hWnd);
             pThread->m_pMainWnd = &threadWnd;
        }
    }
    CATCH_ALL(e)
    {
        // Note: DELETE_EXCEPTION(e) not required.

        // exception happened during thread initialization!!
        TRACE(traceAppMsg, 0, "Warning: Error during thread initialization!\n");

        // set error flag and allow the creating thread to notice the error
        threadWnd.Detach();
        pStartup->bError = TRUE;
        VERIFY(::SetEvent(pStartup->hEvent));
        AfxEndThread((UINT)-1, FALSE);
        ASSERT(FALSE);  // unreachable
    }
    END_CATCH_ALL
 
    // pStartup is invlaid after the following
    // SetEvent (but hEvent2 is valid)
    HANDLE hEvent2 = pStartup->hEvent2;
 
    // allow the creating thread to return from CWinThread::CreateThread
    VERIFY(::SetEvent(pStartup->hEvent));
 
    // wait for thread to be resumed
    VERIFY(::WaitForSingleObject(hEvent2, INFINITE) == WAIT_OBJECT_0);
    ::CloseHandle(hEvent2);
 
    // first -- check for simple worker thread
    DWORD nResult = 0;
    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();
    }

    // cleanup and shutdown the thread
    threadWnd.Detach();
    AfxEndThread(nResult);
 
    return 0;   // not reached
}

      总的来说,MFC提供的“UI线程”,默认为线程实现了一个带OnIdle机制的消息循环,同时,它Attach了应用程序主窗口,m_pMainWindow被设为了应用程序主窗口,它在OnIdle以及ProcessMessageFilter中被用到。

      注意到在_AfxThreadEntry中有一行AfxInitThread,这里面注册了一个消息钩子,钩子回调函数里面会调用ProcessMessageFilter。当处于帮助模式的时候,这个函数会向m_pMainWindow发送code为ID_HELP的WM_COMMAND消息。

三、MFC中的工作线程

      工作线程由另一个AfxBeginThread启动:

CWinThread* AFXAPI AfxBeginThread(AFX_THREADPROC pfnThreadProc, LPVOID pParam,
    int nPriority, UINT nStackSize, DWORD dwCreateFlags,
    LPSECURITY_ATTRIBUTES lpSecurityAttrs)
{
#ifndef_MT
    pfnThreadProc;
    pParam;
    nPriority;
    nStackSize;
    dwCreateFlags;
    lpSecurityAttrs;
 
    return NULL;
#else
    ASSERT(pfnThreadProc != NULL);
 
    CWinThread* pThread = DEBUG_NEW CWinThread(pfnThreadProc, pParam);
    ASSERT_VALID(pThread);
 
    if (!pThread->CreateThread(dwCreateFlags|CREATE_SUSPENDED, nStackSize,
        lpSecurityAttrs))
    {
        pThread->Delete();
        return NULL;
    }
    VERIFY(pThread->SetThreadPriority(nPriority));
    if (!(dwCreateFlags & CREATE_SUSPENDED))
        VERIFY(pThread->ResumeThread() != (DWORD)-1);
 
    return pThread;
#endif//!_MT)
}
      它调用了 CWinThread 的如下构造函数:
CWinThread::CWinThread(AFX_THREADPROC pfnThreadProc, LPVOID pParam)
{
    m_pfnThreadProc = pfnThreadProc;
    m_pThreadParams = pParam;
 
    CommonConstruct();
}
然后同样用 CWinThread::CreateThread创建线程。新线程的入口函数同样为_AfxThreadEntry。与上例不同,这时,程序进入这个if 判断的第一个分支:
// first -- check for simple worker thread
    DWORD nResult = 0;
    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();
    }

      直接调用我们传入的线程函数,而不再进入CWinThread::Run。这里,m_pMainWindow的处理与上例相同。

四、MFC中的UI线程与工作线程的异同

      综上,我们可以看到,MFC里的UI线程里,CWinThread实现了一个消息循环,这是工作线程所不具备的。除此之外,差异之处很寥寥。从MFC代码里来看,MFC的开发者对两者的称呼只是“simple worker thread”和“thread with message loop”,事实上两者的代码层面的区别也正是如此。并且,CWinThread::Run被声明为虚的,这意味着我们可以覆盖它——同时在自己的版本里不实现消息循环。

      而MSDN里,将两个_AfxBeginThread的使用分别称为创建“User Interface Thread”和创建“Worker Thread”。
























  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值