如何正确地将参数传递给pthread

How to correctly pass an argument to a pthread

本文关键字:pthread 参数传递 正确地      更新时间:2023-10-16

我试图找出一种方法,让我的程序产生两个蜂鸣声在同一时间使用Beep()函数在windows.h库。我决定使用几个pthreads来完成这个任务。然而,我在创建一组尚可的参数时遇到了麻烦。

我对编程很陌生,不太了解type_casting或void*语法/规则/目的。我知道它是指向任意类型数据的指针。我希望尽可能多地了解多线程和void,因为它们看起来非常有用。如果有人能建议一些好的文章或教程(为初学者制作的),我将非常感激。

#include <iostream>
#include <pthread.h>
#include <windows.h>
using namespace std;

 void *tone(void *note)
 {
     double freq;
     freq = double(note);
     Beep(freq, 5000);
 }
 int main()
 {
     double C5 = 523.25;
     double D5 = 587.33;
     double E5 = 659.25;
     double F5 = 698.46;
     double G5 = 783.99;
     double A5 = 880.00;
     double B5 = 987.77;
     double C6 = 1046.50;
     pthread_t tone1;
     pthread_t tone2;
     pthread_create(&tone1, NULL, tone, (void *) C5);
 }

同时,我希望我的代码张贴正确。我以前只做过一次。

首先,你应该意识到pthread库(又名POSIX线程)是POSIX系统的线程库,它是当今大多数现代操作系统,除了Windows的;但是<windows.h>是一个非常Windows的头文件,所以你不应该在同一个程序中混合这两个,除非你是针对模拟或兼容层或类似的(例如在Windows上使用Cygwin,或在Linux上使用Wine)开发。

无论您使用哪个线程库,答案都是相同的。POSIX的pthread_create()和Windows的CreateThread()/_beginthreadex()都接受一个void*参数,该参数传递给线程过程。但是,如果你不想传递一个真正的指针值,你就不必这样做了——操作系统把它当作一个不透明的blob,不加修改地传递它。因此,只要要传递的数据不大于指针,就可以在传入时将其强制转换为指针,然后在另一端将其强制转换回来。

例如,如果你想传递一个整数给线程过程:

int theAnswer = 42;
pthread_create(..., &thread_proc, (void *)(intptr_t)theAnswer);
...
void *thread_proc(void *param) {
    int theAnswer = (int)(intptr_t)param;  // Contains 42
    ...
}

你的例子传递了一个double值,它在64位系统上可能与指针的大小相同,所以你可以使用union;但它肯定会比32位系统上的指针大。

为了传递超过指针值的数据给线程过程,你需要把这些数据放在内存的某个地方,然后传递一个指向这些数据的指针。棘手的部分是管理内存,特别是在使用同一个线程过程创建多个线程的情况下。

一个常见的错误是在堆栈上分配该数据,并在循环中创建几个线程,这些线程的参数指向该堆栈数据。然而,所有这些线程将指向相同的堆栈数据,并且它们最终读取的实际数据成为具有许多未定义行为的大竞争条件。

管理内存的正确的方法是为每个线程动态分配内存,然后让新线程负责释放内存。每个线程都有自己的数据副本,没有内存泄漏,也没有竞争条件。

这里有一个例子,无论double是否大于指针(即32位和64位系统):

// Allocate dynamic memory for the thread parameter.  If you want to pass more
// than one value, create a struct instead.
double *theAnswer = new double(42.0);
pthread_create(..., &thread_proc, theAnswer);
...
void *thread_proc(void *param) {
    // Order of operations here is important: dereference the value first, then
    // free the memory
    double *theAnswerPtr = (double *)param;
    double theAnswer = *theAnswerPtr;
    delete theAnswerPtr;
    // theAnswer is now 42.0, use it
    ...
}