AfxBeginThread源码解析

VC程序员对AfxBeginThread函数一定不会陌生,用它来启动线程也是非常的方便,今天一起来看一下AfxBeginThread的具体实现

我们知道操作系统提供的创建线程函数是CreateThread,AfxBeginThread最终也一定会调用操作系统提供的CreateThread,

那么这个调用链是什么样的呢?

大家都知道进程内的多个线程是共享地址空间,那么当防止多线程互相影响,需要各个线程互不影响的全局变量时,又是如何处

理的呢,TSL机制是为解决这个问题设计的,那么VC又是如何使用TSL机制的呢?

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)
}

可以看到,AfxBeginThread内部调用了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
}

在CWinThread的CreateThread成员函数内部调用了_beginthreadex,但是线程函数与参数已经变成了_AfxThreadEntry和startup

 

extern "C" uintptr_t __cdecl _beginthreadex(
    void*                    const security_descriptor,
    unsigned int             const stack_size,
    _beginthreadex_proc_type const procedure,
    void*                    const context,
    unsigned int             const creation_flags,
    unsigned int*            const thread_id_result
    )
{
    _VALIDATE_RETURN(procedure != nullptr, EINVAL, 0);

    unique_thread_parameter parameter(create_thread_parameter(procedure, context));
    if (!parameter)
    {
        return 0;
    }

    DWORD thread_id;
    HANDLE const thread_handle = CreateThread(
        reinterpret_cast(security_descriptor),
        stack_size,
        thread_start<_beginthreadex_proc_type>,
        parameter.get(),
        creation_flags,
        &thread_id);

    if (!thread_handle)
    {
        __acrt_errno_map_os_error(GetLastError());
        return 0;
    }

    if (thread_id_result)
    {
        *thread_id_result = thread_id;
    }

    // If we successfully created the thread, the thread now owns its parameter:
    parameter.detach();

    return reinterpret_cast(thread_handle);
}

到这里我们可以看到_beginthreadex内部调用了CreateThread这个API来创建线程,到此,开篇第一个问题基本上可以回答了

 

我们再看一下这几个线程函数

typedef UINT (AFX_CDECL *AFX_THREADPROC)(LPVOID); // AfxBeginThread

typedef unsigned (__stdcall* _beginthreadex_proc_type)(void*); // _beginthreadex

typedef DWORD (WINAPI *PTHREAD_START_ROUTINE)(
    LPVOID lpThreadParameter
    );        // CreateThread

可以看到声明并不完全一致,调用方式也不完全一致,我们看一下是如何传递的

 

CWinThread对象的CreateThread调用_beginthreadex时替换线程函数为_AfxThreadEntry,函数原型与_beginthreadex_proc_type保持一致

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
}

在这里真正调用了我们的AfxBeginThread传过去的函数

nResult = (*pThread->m_pfnThreadProc)(pThread->m_pThreadParams);

 

再看一下_beginthreadex内部调用CreateThread时传递的线程函数thread_start<_beginthreadex_proc_type>

template 
static unsigned long WINAPI thread_start(void* const parameter) throw()
{
    if (!parameter)
    {
        ExitThread(GetLastError());
    }

    __acrt_thread_parameter* const context = static_cast<__acrt_thread_parameter*>(parameter);

    __acrt_getptd()->_beginthread_context = context;

    if (__acrt_is_packaged_app())
    {
        context->_initialized_apartment = __acrt_RoInitialize(RO_INIT_MULTITHREADED) == S_OK;
    }

    __try
    {
        ThreadProcedure const procedure = reinterpret_cast(context->_procedure);

        _endthreadex(invoke_thread_procedure(procedure, context->_context));
    }
    __except (_seh_filter_exe(GetExceptionCode(), GetExceptionInformation()))
    {
        // Execution should never reach here:
        _exit(GetExceptionCode());
    }

    // This return statement will never be reached.  All execution paths result
    // in the thread or process exiting.
    return 0;
}

在这个模板函数就是真正的线程函数

 

现在我们来看下第二个疑问,我们回到CWinThread对象的CreateThread,注意到 startup.pThreadState = AfxGetThreadState(); 

_AFX_THREAD_STATE* AFXAPI AfxGetThreadState()
{
	_AFX_THREAD_STATE *pState =_afxThreadState.GetData();
	ENSURE(pState != NULL); 
	return pState;
}

THREAD_LOCAL(_AFX_THREAD_STATE, _afxThreadState)

#define THREAD_LOCAL(class_name, ident_name) \
	AFX_COMDAT CThreadLocal ident_name;

_AFX_THREAD_STATE 是MFC的线程状态类,具体定义这里略过

主要看一下CThreadLocal<_AFX_THREAD_STATE >的全局实例_afxThreadState

template
class CThreadLocal : public CThreadLocalObject
{
// Attributes
public:
	AFX_INLINE TYPE* GetData()
	{
		TYPE* pData = (TYPE*)CThreadLocalObject::GetData(&CreateObject);
		ENSURE(pData != NULL);
		return pData;
	}
	AFX_INLINE TYPE* GetDataNA()
	{
		TYPE* pData = (TYPE*)CThreadLocalObject::GetDataNA();
		return pData;
	}
	AFX_INLINE operator TYPE*()
	{ 
		return GetData(); 
	}
	AFX_INLINE TYPE* operator->()
	{ 
		return GetData(); 
	}

// Implementation
public:
	static CNoTrackObject* AFXAPI CreateObject()
		{ return new TYPE; }
};

class AFX_NOVTABLE CThreadLocalObject
{
public:
// Attributes
	CNoTrackObject* GetData(CNoTrackObject* (AFXAPI* pfnCreateObject)());
	CNoTrackObject* GetDataNA();

// Implementation
	int m_nSlot;
	~CThreadLocalObject();
};

这段代码要求TYPE必须得继承CNoTrackObject,看源码也确实是这样class _AFX_THREAD_STATE : public CNoTrackObject

CThreadLocal的m_nSlot成员记录一个使用的槽号,在GetData的实现可以看到

CNoTrackObject* CThreadLocalObject::GetData(
	CNoTrackObject* (AFXAPI* pfnCreateObject)())
{
    ENSURE(pfnCreateObject);

	if (m_nSlot == 0)
	{
		if (_afxThreadData == NULL)
		{
			_afxThreadData = new(__afxThreadData) CThreadSlotData;
			ENSURE(_afxThreadData != NULL);
		}
		m_nSlot = _afxThreadData->AllocSlot();
		ENSURE(m_nSlot != 0);
	}
	CNoTrackObject* pValue = static_cast(_afxThreadData->GetThreadValue(m_nSlot));
	if (pValue == NULL)
	{
		// allocate zero-init object
		pValue = (*pfnCreateObject)();

		// set tls data to newly created object
		_afxThreadData->SetValue(m_nSlot, pValue);
		ASSERT(_afxThreadData->GetThreadValue(m_nSlot) == pValue);
	}
	return pValue;
}

CThreadSlotData* _afxThreadData;

这里可以看到每个线程都把_AFX_THREAD_STATE存在了槽号为m_nSlot的位置,看到这里大致清晰了

inline void* CThreadSlotData::GetThreadValue(int nSlot)
{
	EnterCriticalSection(&m_sect);
	ASSERT(nSlot != 0 && nSlot < m_nMax);
	ASSERT(m_pSlotData != NULL);
	ASSERT(m_pSlotData[nSlot].dwFlags & SLOT_USED);
	ASSERT(m_tlsIndex != (DWORD)-1);
	if( nSlot <= 0 || nSlot >= m_nMax ) // check for retail builds.
	{
		LeaveCriticalSection(&m_sect);
		return NULL;
	}

	CThreadData* pData = (CThreadData*)TlsGetValue(m_tlsIndex);
	if (pData == NULL || pData->pData == NULL || nSlot >= pData->nCount)
	{
		LeaveCriticalSection(&m_sect);
		return NULL;
	}
	void* pRetVal = pData->pData[nSlot];
	LeaveCriticalSection(&m_sect);
	return pRetVal;
}

void CThreadSlotData::SetValue(int nSlot, void* pValue)
{
	EnterCriticalSection(&m_sect);
	ASSERT(nSlot != 0 && nSlot < m_nMax);
	ASSERT(m_pSlotData != NULL);
	ASSERT(m_pSlotData[nSlot].dwFlags & SLOT_USED);
	if( nSlot <= 0 || nSlot >= m_nMax ) // check for retail builds.
	{
		LeaveCriticalSection(&m_sect);
		return;
	}

	CThreadData* pData = (CThreadData*)TlsGetValue(m_tlsIndex);

每个线程都使用TSL机制来存取_AFX_THREAD_STATE,保证多个线程获取到各自的_AFX_THREAD_STATE,我们自己也可以

使用这种机制来解决多个线程使用互不影响的全局变量

当然,vs下也可以这样__declspec(thread) int var= 0;

你可能感兴趣的:(VC++)