函数的地址如何传递给 std::thread

用户13465525

我正在调试一个std::thread用于运行函数的多线程应用程序我在调试时到达以下代码。

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

但我无法理解函数的地址是如何传递给CreateThreadAPI 的。为什么使用thread_start<_beginthreadex_proc_type, true>,以及如何通过此语句计算函数的地址以便由线程运行?

生锈

显示的代码(_beginthreadex函数)是 VC++ CRT 的一部分(可以在例如 中找到
C:\Program Files (x86)\Windows Kits\10\Source\10.0.17763.0\ucrt\startup\thread.cpp)。

的实例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 创建这样一个实例:

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),
        &parameter.get()->_module_handle);

    return parameter.detach();
}

thread_start 是调用线程过程的模板函数:

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

它本质上调用invoke_thread_procedure,它只是调用procedure,传入context

static __forceinline unsigned int invoke_thread_procedure(
    _beginthreadex_proc_type const procedure,
    void*                    const context
    ) throw()
{
    return procedure(context);
}

调用周围的代码会做一些簿记以保持 CRT 的一致性,例如它会在退出时自动清理线程 ( _endthreadex)。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

将类的成员函数传递给std :: thread

'std :: thread'如何确定传递给构造函数的可变参数的数量

如何将可变参数传递给std :: thread?

将模板args传递给std :: thread

将std :: shared_ptr传递给函数对象到std :: thread

如何使用std :: thread?

如何终止std :: thread?

使用删除的函数'std :: thread :: thread(const std :: thread&)'

显示 std::thread 函数传递的错误数据。

将对象传递给Lambda std :: thread(C ++)中的函数:尝试使用已删除的函数

是否可以将重载方法传递给std :: thread

将参数传递给std :: thread的区别,C ++

将参数传递给std :: thread包装器

如何从std :: thread更改GUI?

std :: thread通过引用传递调用复制构造函数

如果容器不是调用函数中的引用,则通过std :: thread将引用传递给迭代器失败

将模板(无规范)传递给std :: thread()会给出错误:<不可解析的重载函数类型>匹配错误

将std :: sub_match作为参数传递给std :: thread时出了什么问题?

错误:使用已删除的函数'std :: thread :: thread(const std :: thread&)'

std :: thread :: thread试图引用已删除的函数

没有参数的std :: thread构造函数

在函数方法中使用std :: thread

用不同的参数函数运行std :: thread

std :: thread Args ...列表中的函数指针

可连接std :: thread的析构函数

std :: thread构造函数(变量数量)

如何将std :: unique_ptr传递给函数

如何将r值std :: vector传递给函数?

如何将对象传递给 std::accumulate 函数?