header detail 1
header detail 2
机甲先锋活动站 - 科幻竞技游戏专属平台
机甲先锋活动站 - 科幻竞技游戏专属平台

一、_beginthreadex

Home 2025-12-04 06:00:10 一、_beginthreadex
联盟对抗专区

_beginthreadex是ucrt提供的创建线程的接口,_beginthreadex在内部调用了windows系统提供的CreateThread接口,_endthreadex的作用是显式的结束线程,实际上线程结束的时候会自动调用这个接口。

ucrt源码中_beginthreadex和_endthreadex的实现在下面这个路径

C:\Program Files (x86)\Windows Kits\10\Source\10.0.10150.0\ucrt\startup\thread.cpp

一、_beginthreadex

_beginthreadex主要包括2个步骤,第一步创建线程参数,第二步调用系统接口创建线程。

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

// 1 创建线程参数

unique_thread_parameter parameter(create_thread_parameter(procedure, context));

if (!parameter)

{

return 0;

}

// 2 调用系统接口创建线程

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

}

第一步调用create_thread_parameter创建线程参数

线程参数是如下所示的结构体,包括了线程的入口方法_procedure和方法的入参_context,从代码注释可以看到线程句柄_thread_handle只会在调用_beginthread创建线程的时候进行初始化,而调用_beginthreadex创建线程不会初始化.

这就是为什么调用_beginthread创建线程,当线程结束之后调用CloseHandle会导致0xC0000008错误(An invalid handle was specified),而使用_beginthreadex创建线程不会导致这个问题,后面在_endthreadex的源码中就可以看到详细原因。

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;

第二步是调用系统接口CreateThread创建线程,这里需要注意的是传入的方法入口并不是我们传递给_beginthreadex的入参procedure,而是对procedure进行了装饰,实际的方法入口变成了thread_start,thread_start是一个模板函数,代码如下

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

_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;

}

从上面的代码中可以看到thread_start会在我们传入的方法结束之后调用_endthreadex,实际上从代码中可以看到_beginthread的实现中也是通过thread_start装饰线程执行的函数,所以_beginthread在结束后也是调用的_endthreadex,而不是调用_endthread。实际上后面可以看到_endthreadex和_endthread的实现几乎是一样的,调用两个方法实际上是一样的。

另外thread_start内部使用SEH机制捕获异常。

_beginthread和_beginthreadex的入参不一样,但是内部实现是类似的,_beginthreadex可以指定新建线程的初始状态,而_beginthread不能指定状态,默认使用CREATE_SUSPENDED使得线程创建之后挂起,然后再通过ResumeThread启动。

二、_endthreadex

extern "C" void __cdecl _endthread()

{

return common_end_thread(0);

}

extern "C" void __cdecl _endthreadex(unsigned int const return_code)

{

return common_end_thread(return_code);

}

从上面可以看到_endthreadex和_endthread都调用了common_end_thread,只不过入参不同。由于_beginthreadex没有初始化线程参数中持有的线程句柄parameter->_thread_handle,因此在线程结束后自动调用_endthreadex的过程中,只有_beginthread创建的线程会执行到CloseHandle。

如果有DLL引用的话,通过FreeLibraryAndExitThread释放对DLL的引用并且退出线程,否则直接调用ExitThread。

static void __cdecl common_end_thread(unsigned int const return_code) throw()

{

__acrt_ptd* const ptd = __acrt_getptd_noexit();

if (!ptd)

{

ExitThread(return_code);

}

__acrt_thread_parameter* const parameter = ptd->_beginthread_context;

if (!parameter)

{

ExitThread(return_code);

}

if (parameter->_initialized_apartment)

{

__acrt_RoUninitialize();

}

if (parameter->_thread_handle != INVALID_HANDLE_VALUE && parameter->_thread_handle != nullptr)

{

// 只有_beginthread创建的线程能执行到这里

CloseHandle(parameter->_thread_handle);

}

if (parameter->_module_handle != INVALID_HANDLE_VALUE && parameter->_module_handle != nullptr)

{

FreeLibraryAndExitThread(parameter->_module_handle, return_code);

}

else

{

ExitThread(return_code);

}

}

Reference:

1、CreateThread function (processthreadsapi.h) - Win32 apps | Microsoft Docs

2、_beginthread, _beginthreadex | Microsoft Docs

3、_endthread, _endthreadex | Microsoft Docs

Post navigation

  • Prev Post MongoDB mongostat和mongotop性能监控命令的用法(非常详细)
Copyright © 2088 机甲先锋活动站 - 科幻竞技游戏专属平台 All Rights Reserved.
友情链接