【问题标题】:Define thread in C++在 C++ 中定义线程
【发布时间】:2012-10-25 07:09:56
【问题描述】:

如何使用_beginthreadex() 启动线程,使其执行void myFunction(wchar_t *param);?我尝试使用这个:

_beginthread(NULL, 0, myFunction, L"someParam", 0, &ThreadID);

但是有编译错误:

错误 C2664:“beginthreadex”:无法将参数 3 从“void (_cdecl *)(wchar_t *)”转换为“unsigned int (__stdcall *)(void *)”。

如何解决此错误?我似乎能够做到_beginthread((void(*)(void*))myFunction, 0 , (void *)L"someParam");。但是对于_beginthreadex(),这些演员表似乎不起作用。做什么 我需要去做? 此代码不输出任何内容。怎么了?

unsigned int __stdcall myFunction( void *someParam )
{
    printf("Hello world!");
    return 0;
}


int _tmain(int argc, _TCHAR* argv[])
{
    _beginthreadex(NULL, 0, myFunction, L"param", 0, NULL);
    return 0;
}

【问题讨论】:

  • @Andres Jaan Tack 这是 WINAPI 项目
  • 如果问题与应在标签中列出的特定库/api 相关,以更好地帮助识别范围。

标签: c++ winapi


【解决方案1】:

_beginthreadex 需要使用__stdcall calling convention 的函数,而不是默认的__cdecl 调用约定。要修复它,请使用 __stdcall 调用约定声明您的线程过程:

unsigned int __stdcall myFunction(void *arg)
{
    ...
}

不要将您传递的函数指针转换为_beginthread_beginthreadexa function pointer cast is a bug waiting to happen

【讨论】:

  • 我应该从你描述的函数签名中返回一些东西吗?
  • 是的,你应该返回一些作为线程退出状态的东西(如果你没有别的东西,则返回 0)。你遇到了什么错误?
  • 我尝试返回 0。接下来出现三个错误:错误 1 ​​错误 C2664: '_beginthreadex' : 无法将参数 3 从 'void *(__stdcall *)(void *)' 转换为 ' unsigned int (__stdcall *)(void *)' 2 IntelliSense:“uintptr_t”类型的值不能分配给“HANDLE”类型的实体 3 IntelliSense:“void *(__stdcall *)(void *folder) 类型的参数" 与 "unsigned int (__stdcall *)(void *)" 类型的参数不兼容
  • 抱歉,我的返回类型错误——它应该是unsigned int,而不是void *。您还需要将_beginthreadex 的返回值从uintptr_t 转换为HANDLE
【解决方案2】:

每个 Microsoft CRT 线程启动函数的函数原型要求是:

您还应该注意两者之间的区别。

  • _beginthread:分配一个新线程并调用作为参数传递的线程过程。使用此 API,线程创建参数有些受限。这个函数的返回值是一个uintptr_t,但实际上是一个HANDLE类型的Windows线程句柄。它必须转换为一个 HANDLE 变量才能在 WaitForSingleObject 等函数中使用。当线程过程通过正常的函数退出完成时,运行时会自动为您关闭线程句柄。 这很重要。虽然这个函数像_beginthreadex一样返回一个线程句柄,但可以想象,线程在您可以对句柄执行任何操作(例如等待、挂起等)之前开始并结束。一旦线程过程完成,RT 将关闭句柄,因此保存初始返回句柄的局部变量现在无效。

  • _beginthreadex:分配一个新线程并调用作为参数传递的线程过程。此版本允许对线程的创建方式进行更多控制,包括堆栈大小、初始挂起状态等。此函数的返回值是 uintptr_t,但实际上是 HANDLE 类型的 Windows 线程句柄。它必须转换为一个 HANDLE 变量才能在 WaitForSingleObject 等函数中使用。当线程过程通过正常函数退出完成时,运行时不会自动为您关闭线程句柄. 您有责任关闭此函数返回的线程句柄,并应在不再需要时立即关闭

使用哪个:如果您不需要在等待函数等中使用线程句柄,并且没有特殊的线程创建需求(例如创建具有初始暂停状态的线程),请使用 _beginthread。当您需要可等待的线程句柄、更好地控制创建参数等时,请使用_beginthreadex

编辑:OP 示例

#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <process.h>

unsigned int __stdcall MyThread(void *p)
{
    _tprintf(_T("%s\n"), p);
    _tprintf(_T("Thread finishing!\n"));
    return 0;
}

int _tmain(int argc, TCHAR *argv[])
{
    HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, MyThread, _T("Hello, World!"), 0, NULL);
    if (hThread != INVALID_HANDLE_VALUE)
    {
        WaitForSingleObject(hThread, INFINITE);
        CloseHandle(hThread);
        _tprintf(_T("Thread finished!\n"));
    }
    return 0;
}

【讨论】:

  • 它有效,但在 Richter 的书“Windows via c++”中,我读到我可以使用类似 HANDLE hThread = _beginthreadex(...);就我而言,这不起作用,但是带有您描述正确的签名的单个 _beginthreadex() 。有什么问题?
  • 将返回结果转换为句柄:HANDLE hThread = (HANDLE)_beginthreadex(...),但正如我上面所说,如果您不需要 处理等待函数之类的句柄,只需使用_beginthread 代替。如果您确实需要_beginthreadex 的额外灵活性,请务必在不再需要时使用CloseHandle() 关闭返回的句柄。
  • 对不起,它只是编译没有错误。下一个代码 unsigned int __stdcall myFunction( void someParam ) { printf("Hello world!");返回0; } int _tmain(int argc, _TCHAR argv[]) { _beginthreadex(NULL, 0, myFunction, L"param", 0, NULL);返回0; }
  • 此代码不输出任何内容。稍后我会将其添加到我的问题中
  • @Vsevywniy 更新为包含调用 _beginthreadex 的示例,以及如何使用(和关闭)返回的线程句柄。
【解决方案3】:

与上面的其他答案一样,不要忘记在 _tmain 退出之前等待线程完成,否则您可能看不到任何输出。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-06-18
    • 2016-02-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多