[英]How address of a function will pass to the std::thread
I was debugging a multithreaded application that is using std::thread
to run a function.我正在调试一个使用std::thread
运行 function 的多线程应用程序。 I reach to the following code when debugging.调试时我达到以下代码。
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<LPSECURITY_ATTRIBUTES>(security_descriptor),
stack_size,
thread_start<_beginthreadex_proc_type, true>,
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<uintptr_t>(thread_handle);
}
But I couldn't understand how address of the function passed to the CreateThread
API.但我不明白 function 的地址如何传递给CreateThread
API。 Why is it using thread_start<_beginthreadex_proc_type, true>
, and how the address of a function will calculate by this statement in order to run by threads?为什么它使用thread_start<_beginthreadex_proc_type, true>
,以及 function 的地址将如何通过此语句计算以便由线程运行?
The code shown ( _beginthreadex
function) is part of VC++ CRT (can be found in, eg显示的代码( _beginthreadex
函数)是 VC++ CRT 的一部分(可以在例如C:\Program Files (x86)\Windows Kits\10\Source\10.0.17763.0\ucrt\startup\thread.cpp
). C:\Program Files (x86)\Windows Kits\10\Source\10.0.17763.0\ucrt\startup\thread.cpp
)。
An instance of unique_thread_parameter
is a structure that holds the thread procedure
pointer, the context
argument, and thread and module HANDLE
s: unique_thread_parameter
的实例是一个包含线程procedure
指针、 context
参数以及线程和模块HANDLE
的结构:
// corecrt_internal.h
typedef struct __acrt_thread_parameter
{
// The thread procedure and context argument
void* _procedure;
void* _context;
// The handle for the newly created thread. This is initialized only from
// _beginthread (not _beginthreadex). When a thread created via _beginthread
// exits, it frees this handle.
HANDLE _thread_handle;
// The handle for the module in which the user's thread procedure is defined.
// This may be null if the handle could not be obtained. This handle enables
// us to bump the reference count of the user's module, to ensure that the
// module will not be unloaded while the thread is executing. When the thread
// exits, it frees this handle.
HMODULE _module_handle;
// This flag is true if RoInitialized was called on the thread to initialize
// it into the MTA.
bool _initialized_apartment;
} __acrt_thread_parameter;
// thread.cpp
using unique_thread_parameter = __crt_unique_heap_ptr<
__acrt_thread_parameter,
thread_parameter_free_policy>;
create_thread_parameter
creates such an instance: create_thread_parameter
创建这样一个实例:
static __acrt_thread_parameter* __cdecl create_thread_parameter(
void* const procedure,
void* const context
) throw()
{
unique_thread_parameter parameter(_calloc_crt_t(__acrt_thread_parameter, 1).detach());
if (!parameter)
{
return nullptr;
}
parameter.get()->_procedure = procedure;
parameter.get()->_context = context;
// Attempt to bump the reference count of the module in which the user's
// thread procedure is defined, to ensure that the module will stay loaded
// as long as the thread is executing. We will release this HMDOULE when
// the thread procedure returns or _endthreadex is called.
GetModuleHandleExW(
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
reinterpret_cast<LPCWSTR>(procedure),
¶meter.get()->_module_handle);
return parameter.detach();
}
thread_start
is a template function that invokes the thread procedure: thread_start
是调用线程过程的模板 function:
template <typename ThreadProcedure>
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_get_begin_thread_init_policy() == begin_thread_init_policy_ro_initialize)
{
context->_initialized_apartment = __acrt_RoInitialize(RO_INIT_MULTITHREADED) == S_OK;
}
__try
{
ThreadProcedure const procedure = reinterpret_cast<ThreadProcedure>(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;
}
It essentially calls invoke_thread_procedure
, which just invokes procedure
, passing in context
:它本质上调用invoke_thread_procedure
,它只是调用procedure
,传入context
:
static __forceinline unsigned int invoke_thread_procedure(
_beginthreadex_proc_type const procedure,
void* const context
) throw()
{
return procedure(context);
}
The code around the calls does some bookkeeping to keep the CRT consistent, for example it automatically cleans up the thread upon exit ( _endthreadex
).调用周围的代码会进行一些记账以保持 CRT 一致,例如它会在退出时自动清理线程 ( _endthreadex
)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.