【问题标题】:Create thread is not accepting the member function创建线程不接受成员函数
【发布时间】:2010-05-23 13:40:15
【问题描述】:

我正在尝试为网络编程创建一个类。这将创建一个带有线程的通用套接字。

但是当我尝试使用 createthread() 来创建线程时。第三个论点是产生错误。从网上我知道我不能使用成员函数作为 createthread() 的参数。

我有什么办法可以做到这一点吗?

【问题讨论】:

  • 问题在于“成员函数”实际上是两件事:一个指向函数的指针,以及一个指向要应用它的实例的指针。 C++ 不能把它变成一个函数指针(好吧,它可以,但它不够努力)。
  • “还不够努力”?你究竟如何建议它这样做?一般来说,将两个字的数据打包到一个字的存储中是相当困难的,而且这甚至不考虑另一个问题,即成员函数和非成员具有不同的调用约定。

标签: c++ windows callback function-pointers


【解决方案1】:

处理此问题的最简单方法是创建一个“存根”函数,该函数回调到您的类中。

UINT tid
HANDLE hThread = CreateThread(NULL, 0, myThreadStub, this, 0, &tid);

....

unsigned long WINAPI myThreadStub(void *ptr) 
{
    if (!ptr) return -1;
    return ((MyClass*)ptr)->ThreadMain();
}

CreateThread() 允许您将参数传递给线程函数(CreateThread() 调用的参数 4)。您可以使用它来传递指向您的类的指针。然后,您可以让线程存根将该指针转换回正确的类型,然后调用成员函数。你甚至可以让“myThreadStub”成为“MyClass”的static成员,允许它 访问私有成员和数据。

如果您安装了 boost,则可以使用 boost::bind 来执行此操作,而无需创建存根函数。我从来没有在 Windows 上尝试过,所以我不能肯定它会起作用(因为回调函数必须是 WINAPI 调用)但如果它确实起作用,它看起来像:

HANDLE hThread = CreateThread(NULL, 0, boost::bind(&MyClass::ThreadFunction, this), NULL, 0, &tid);

其中线程函数是一个非静态成员函数,它接受一个 void * 参数。

【讨论】:

  • 使用“1010”按钮,或者只缩进4个空格。
  • Boost.Bind 在这里没有帮助 - 它返回函子,而不是函数指针。
【解决方案2】:

有一个简单的方法可以解决这个问题。

看看ThreadProc callback function

DWORD WINAPI ThreadProc( __in LPVOID lpParameter );

现在CreateThread function:

处理 WINAPI CreateThread( __in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes, __in SIZE_T dwStackSize, __in LPTHREAD_START_ROUTINE lpStartAddress, __in_opt LPVOID lpParameter, __in DWORD dwCreationFlags, __out_opt LPDWORD lpThreadId );

使用静态方法作为线程过程,但从成员方法中调用它,并将对象指针传递给它:

#include 我的班级{ 上市: 无效 CreateThreadForObject() { LPSECURITY_ATTRIBUTES security_attributes = 0; SIZE_T stack_size = 0; LPTHREAD_START_ROUTINE start_routine = &MyClass::ThreadProcForObject; LPVOID 参数 = this; DWORD 创建标志 = 0; LPDWORD thread_id = 0; CreateThread(security_attributes, stack_size, start_routine, param, 创建标志,线程ID); } 私人的: 静态 DWORD WINAPI ThreadProcForObject(LPVOID 参数) { MyClass* instance = reinterpret_cast(param); 如果(!实例)返回1; // ... 返回0; } };

对不起,我只是没有足够的时间来写一个好的例子。但我想你理解的方式。

【讨论】:

  • 我会将此标记为 +10。这是我发现的第一个不向公众公开类内部的方法。我是 C# 开发人员,C# 为我做了很多有趣的事情。
  • 如果你知道的话,可以用 lambda 做同样的事情吗?
【解决方案3】:

迷路了,我明白了,事实上,在CreateThread中,如果你通过了socket,那么就没有问题了。因为 CreateThread 正在 照顾socket。但是,如果您作为具有该 socket 的对象传递,那么 CreateThread不关心 socket ,并在 新线程 中以 invalid socket 结束。

下面的成功代码

SOCKET s=socket(....);
bind(s,...);
listen(s,...);
SOCKET temp=accept(s,(sockaddr *)&addrNew,&size);
DWORD threadId;
HANDLE thread=CreateThread(NULL,0,&MyThreadFunction,(LPVOID)(temp),0,&threadId);

【讨论】:

  • 没有理由传递一个包含套接字作为成员变量的对象指针不起作用。如果套接字变量变得无效,那是由于您的一些编码错误。 CreateThread 没有套接字的概念,或者如何“照顾”它们。
  • 我不这么认为。我正在研究这个将近两个星期。你可以检查一下。我的假设是套接字需要一些环境,当你通过 CreateThread 对其进行暂停时,createthread 也会传递环境。但是,如果您传递一个对象(具有该套接字),那么在线程中它是无效的。我检查了它。并且 MSDN 论坛还显示了在 createthread 中传递套接字的示例。似乎他们隐藏了 createthread 的一些缺点。
  • 解释完全不正确;见布鲁克迈尔斯的回应。 -1 为此。
  • @michael 无论你问什么,我已经在 4 年前完成了(请参阅我评论中的日期)。我也发布了代码。如果您在代码中指出问题,我会很高兴。
  • @prabhakaran CreateThread 不知道 SOCKET 是什么。它不在乎。在您的回答中,您说“因为 CreateThread 正在处理那个套接字。”这是不正确的。您可能的意思或应该真正的意思是,当您将套接字作为状态参数传递给CreateThread 时,回调的主体正在处理该套接字。如果您传递除SOCKET 以外的任何内容并在回调中强制转换为SOCKET,则可能会产生访问冲突错误。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-23
  • 1970-01-01
  • 2023-03-18
  • 1970-01-01
  • 2016-07-02
  • 1970-01-01
相关资源
最近更新 更多