【问题标题】:Why does a function accept a void pointer?为什么函数接受 void 指针?
【发布时间】:2015-12-05 21:31:01
【问题描述】:

我正在尝试理解 pthread。

但是需要创建线程方法如下:

void *SomeMethod(void* x)
{
    //Do Something
}

为什么需要创建一个接受void 指针的函数?我们不能将 pthread 与这样的函数一起使用吗?

void SomeMethod()
{
}

【问题讨论】:

  • 你可以随便写一个包装器:void* ThreadMethod (void*) { SomeMethod(); return NULL; }
  • 实际上void * foo() 是有效的 C,尽管它已被弃用,因为它没有指定其参数列表。

标签: c++ c pthreads parameter-passing void-pointers


【解决方案1】:

因为pthread_create 函数接受void* (*)(void*) 类型的参数,这是一个接受void* 并返回void* 的函数,所以要使用pthread_create 创建线程,这就是您需要使用的。

pthread_create API 允许您将数据传递到新线程并再次取回数据。如果你不想传递任何东西,你仍然必须满足那个接口,但只需将它传递为 NULL。

仅仅因为你现在不想将任何参数传递给你的新线程并不意味着 API 应该被设计为只支持你当前的用例。使用采用void*(可以选择传递 NULL)的函数编写 API 比使用不带参数的函数编写 API 并要求用户提出自己的传递解决方案要好得多数据到新线程。

在 C++ 中,您可以为新线程使用任何类型的函数,并将所需的任何参数传递给它:

std::thread t(&SomeMethod);

【讨论】:

    【解决方案2】:

    因为他们只想编写一个用于创建线程的函数——获取和返回数据。

    否则他们将不得不写出至少四种可能性。

    你可以忽略输入参数和返回值。

    只是简单的问题

    【讨论】:

      【解决方案3】:

      因为很多时候,我们希望给线程一些可以工作的东西(或配合,或关闭,或其他)。一个很典型的例子就是传入一个类的实例,这样就可以调用类的成员函数了。

      但它可能是各种其他的东西——一个结构,或者指向一些简单数据的指针。

      当然,使用std::thread 无论如何都会隐藏大部分此类内容,您无需担心。我强烈建议一般使用std::thread 而不是pthread。

      【讨论】:

        【解决方案4】:

        根据pthread_create()的man page可以看出,函数的签名是

         int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                       void *(*start_routine) (void *), void *arg);
        

        第三个参数的类型

        void *(*start_routine) (void *)
        

        这意味着,它需要一个指向返回类型为void * 并接受void * 参数的函数的指针。所以,我们需要相应地定义线程函数。

        也就是说,关于 void 指针用于参数传递的用法,引用 C11,第 §6.3.2.3 章

        指向void 的指针可以转换为或从指向任何对象类型的指针转​​换。指向任何对象类型的指针可以转换为指向void 的指针并再次返回;结果应与原始指针比较。

        如我们所见,void 指针用于将任何类型的数据传递给线程函数,前提是它会被转换回函数内部的实际类型。

        另外,FWIW,不要试图偏离所需的函数指针签名,因为标准明确规定

        [....] 如果转换后的指针用于调用类型与引用类型不兼容的函数,则行为未定义。如果您不想(不需要)传递任何有效的参数值,您可以随时传递NULL。

        【讨论】:

          【解决方案5】:

          void *SomeMethod(void* x) 用作新线程的入口点。 如果你想向新线程传递一些数据(结构、缓冲区、常规整数等),你应该怎么做?特别是对于这个 entry point 方法接收void* 参数,它可以指向你想要的任何东西。然后在函数体内,你可以将它转换回正确的类型,并由工作线程使用。

          如果您的线程不需要任何其他数据,您可以将 NULL 或 nullptr(对于 C++11)传递给它。

          【讨论】:

            【解决方案6】:

            不,您建议的方法签名不能用作 pthreads 线程的启动函数。如果您将指向此类函数的指针传递给pthread_create(),则编译器应发出警告。如果您运行生成的程序,那么您将调用未定义的行为,因为指针没有正确的类型。实际上,pthreads 库将尝试将参数传递给 start 函数,并期望从它的返回值;如果您提供一个指向函数的指针,该函数既不使用参数也不返回值(两者都是适当的类型),那么程序很可能会崩溃。

            至于为什么 start 函数需要有签名,void * 参数可用于传达任何类型的实际参数,包括包含多个不同值的复合类型,类似地,void * 返回类型可用于传达任何类型的返回值。这几乎是最通用的函数类型了。

            【讨论】:

              猜你喜欢
              • 2018-08-27
              • 1970-01-01
              • 2020-06-22
              • 2011-07-31
              • 1970-01-01
              • 2020-06-11
              • 2013-11-14
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多