【问题标题】:Returning void pointer in pthread在 pthread 中返回 void 指针
【发布时间】:2020-12-07 07:39:39
【问题描述】:

我刚开始使用 pthreads 学习 C++ 中的多线程。我正在使用以下代码:

struct ArgT{
    int a;
    int b;

    ArgT(int a, int b)
        : a(a), b(b)
    {}

    ArgT()
        :a(0), b(0)
    {}
};

void* worker(void* arg)
{
    ArgT* myArg = (ArgT*) arg;
    int* result = new int;

    *result = myArg->a + myArg->b;

    std::cout << "(void*)result: " << (void*) result << std::endl;
    return (void*)result;
}

int main()
{
    ArgT mainArg(2,3);
    pthread_t p;
    int* main_result;

    pthread_create(&p, NULL, worker, (void*)&mainArg);
    pthread_join(p, (void**)&main_result); //??

    std::cout << "main_result: " << main_result << std::endl; 
    std::cout << "&main_result: "<< &main_result << std::endl;
    printf("\nResult = %d\n", *main_result);

    delete main_result;

    return 0;
}

代码输出如下

(void*)result: 0x7f07ac0008c0
main_result: 0x7f07ac0008c0
&main_result: 0x7fffb1aa0588

Result = 5

我的问题是pthread_join() 接受void** 作为第二个参数,它基本上是地址的地址。而我们在worker() 函数中返回void* 类型的地址。这两种类型如何兼容?

【问题讨论】:

  • 因为您正在编写 C++,所以我真的建议您查看 std::thread。或者,如果您期望返回 std::async.
  • 至于void** 问题,它是一种在C 中模拟通过引用传递的方法(POSIX 线程API 是C API)。
  • 感谢您的意见。那是我的下一个问题,使用 std::thread 而不是 pthreads 的主要优势是什么。我主要坚持使用 pthreads,因为我目前正在阅读的书(操作系统:三个简单的部分)用 pthread @Someprogrammerdude 解释了一切
  • 除了它是标准 C++ 库的一部分,因此是可移植的吗?好吧,这通常要容易得多。你可以只拥有void worker(ArgT argument) { ... },然后创建一个像std::thread my_thread(&amp;worker, mainArg); /* Other code... */ my_thread.join(); 这样的线程,不再需要强制转换、类型安全、面向对象、更简单...... ;)
  • 我明白了。我想我会坚持使用 pthreads,直到我完成这本书,也许稍后我可以切换到 std::threads。我主要想学习有关多线程的理论。我认为一旦我对理论相当有能力,我就可以切换 API。感谢您的输入。 @Someprogrammerdude

标签: c++ multithreading pthreads pthread-join


【解决方案1】:

pthread_join() 获取指针变量的地址。该变量将接收线程返回的指针值。它实际上与此相同:

void doIt(void (*f)(), void** ptr)
{
    *ptr = f();
}

void* func()
{
    return ...; 
}

int main()
{
    void *ptr;
    doIt(func, &ptr);
    // ptr holds whatever func() returns...
}

【讨论】:

    【解决方案2】:

    这两种类型如何兼容?

    void**间接通过时,结果是void*类型的左值


    您的程序有未定义的行为。这是一个正确的方法:

    void* void_main_result;
    
    pthread_join(p, &void_main_result);
    
    int* int_main_result = static_cast<int*>(void_main_result);
    std::cout << "\nResult = " << *int_main_result;
    

    您可以通过在参数中分配结果来避免动态分配。这样就不需要返回值了:

    struct ArgRet {
        ArgT args;
        int ret;
    } mainArg;
    
    pthread_create(&p, NULL, worker, &mainArg);
    
    std::cout << "\nResult = " << mainArg.ret;
    
    // in worker
    ArgRet* myArg = static_cast<ArgRet*>(arg);
    myArg->ret = myArg->args.a + myArg->args.b;
    return arg; // or nullptr; doesn't really matter
    

    使用 std::thread 代替 pthread 的主要优势是什么

    std::thread 适用于所有 C++(11 或更高版本)实现。 pthreads 仅适用于 POSIX 系统。 std::thread 的 API 也更容易使用,因为它是用 C++ 而不是 C 编写的。


    附:将指针转换为 void* 是隐式的。我建议不要使用 C 风格的强制转换。

    【讨论】:

    • 感谢您的回答。你能告诉我你认为导致未定义行为的原因吗?我目前正在阅读的书具有相同的实现,并且代码的输出似乎也很好。
    • @HariP19 这是 UB,因为pthread_join 将通过void** 间接分配给左值,在这种情况下,此标准规则适用:If a program attempts to access the stored value of an object through an lvalue of other than one of the following types the behavior is undefined: - the dynamic type of the object, ... 对象的动态类型为int*pthread_join 中用于访问它的左值是void*。那些不是同一类型。该规则有一个允许的其他类型的列表,在这种情况下都不适用。看来这本书有错误。
    猜你喜欢
    • 1970-01-01
    • 2020-06-11
    • 2014-08-16
    • 2014-05-30
    • 2013-03-05
    • 2012-02-26
    • 1970-01-01
    • 2020-05-03
    • 2013-01-08
    相关资源
    最近更新 更多