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;