为什么我不能将 void* 转换回 int* 并取消引用它?

Why cant I cast a void* back to an int* and dereference it?

我试图从我老师的实验室中理解 C 中的线程,这就是他在 class 中让我们做的方式。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>

#define numberlen 1000000
int sum = 0;
int arr[numberlen];


void* Summing(void* param)
{
    int n = *((int*)param); //I'm guessing this is where my issue is but I'm still a first year so i'm not sure.
    int base = 500000 * n;
    for(int i =0; i<500000;i++)
    {
        sum += arr[i+base];
    }
}




int main()
{
    int nonthreadsum = 0;
    pthread_t thread_id_first;
    pthread_t thread_id_second;
    


    srand(time(NULL)); 
    
    for(int i = 0; i<numberlen;i++)
    {
        int r = (rand() % 10);
        arr[i] = r;
        nonthreadsum += r;
    }
    printf("%d\n",nonthreadsum);

    pthread_create(&thread_id_first, NULL, Summing, (void*)0);
    pthread_create(&thread_id_second, NULL, Summing, (void*)1);

    
    printf("%d\n", sum);


    return 1;
}

我一直运行关注的问题是 pthread 函数只允许 void* 作为参数传递给实际函数 运行。否则我只是将 0 或 1 作为参数。如果有人知道解决这个问题的方法,我将不胜感激。

允许(在某些情况下)从整数转换为指针以及从指针转换为整数。问题是那不是你想要做的。

当您调用 pthread_create 时,您将整数值 10 转换为类型 void *。这可以。但是然后在线程函数中你这样做:

int n = *((int*)param); 

您将 void * 参数转换为 int *,然后尝试取消引用该指针。但是你没有传入 int *。您通过了 int。因此,您的程序将值 1 和 0 视为指针(后者实际上是 NULL 指针)。

由于在将值传递给函数时从 int 转换为 void *,因此您希望在函数内部转换回 int

int n = (int)param; 

话虽如此,处理此问题的最佳方法是将实际的 int * 传递给函数并在函数中将其转换回。从 int * 转换为 void * 并返回 在所有情况下都是 明确定义的。

您可以通过在 main 函数中定义变量来动态创建内存来保存这些值:

int *t1_val = malloc(sizeof(*t1_val));
int *t2_val = malloc(sizeof(*t2_val));
*t1_val = 0;
*t2_val = 1;
pthread_create(&thread_id_first, NULL, Summing, t1_val);
pthread_create(&thread_id_second, NULL, Summing, t2_val);

然后像以前一样在线程中使用它们,添加对 free:

的调用
int n = *((int*)param);
free(param);

您也可以通过使用 int 类型的变量并传递它们的地址来在没有动态分配的情况下执行此操作,但是您需要确保在线程尝试时这些变量不会超出范围取消引用指针。

将您的数据地址传递给您的线程

将您的数据地址传递给线程。不要在指针内传递数据。

将数据传递给线程的预期方式是传递指向它的指针。例如传递类型为Type的数据,可以使用:

Type x;
// … Set desired value(s) in x.
pthread_create(&thread_id_first, NULL, Summing, &x);

您可以通过 (void *) &x 但不需要。对于声明为类型 void *.

的参数,在 C 中将自动转换为 void *

那么,在线程中,你应该恢复指针类型:

void *Summing(void *param)
{
    Type *p = param;
    Type x = *p;
    // … Use x.
}

void *(Type *p = param;)指针的转换和指针(Type x = *p;)的使用在上面分两行说明。但是,您可以将它们合并为一行:Type x = * (Type *) param;.

为什么使用 void *

任何类型的数据都可以通过地址传递给线程。但是,C 没有很好的规定可以将任何类型的指针传递给例程。 pthread_create 必须用特定的指针类型声明。 void * 用作泛型指针。所以 pthread_create 有一个 void * 参数,但你可以将任何类型的指针传递给它。

在您的情况下,类型可以是 int,替换上面示例代码中的 Type。给定 int x;&x 将是指向 intint * 的指针。当你传递给pthread_create时,它会自动转换为void *.

然后,在线程中,您需要将其转换回 int *。完成后,您可以访问它指向的 int

传递大量数据

线程传递的数据通常比简单的 int 多。 Type 可以是对象类型。通常,使用结构。一个程序会定义一个结构体,如:

struct foo
{
    int start;
    int end;
    double coefficient;
    double ReferenceData[30];
}

Type 示例一样,可以创建一个所需类型的对象 struct foo x;,用数据填充它,然后将 &x 传递给 pthread_create。然后该线程会将它接收到的void *转换为struct foo *并使用它来访问数据。

在这种情况下,因为数据太多,线程可能不想复制它,而这正是 struct foo x = * (struct foo *) param; 所做的。相反,线程可能只是创建一个正确类型的指针,struct foo *p = param;。然后线程可以通过->运算符使用数据:p->startp->ReferenceData[i]等。

线程也可以通过将结果放入指向数据中,以这种方式将结果传回给它们的创建者。

传递少量数据

要传递少量数据,有一个捷径:您可以创建一个对象并使用复合文字传递其地址:

pthread_create(&thread_id_first, NULL, Summing, & (int) {1});`.

这将创建一个值为 1 的 int 对象,并将其地址传递给 pthread_create。然后线程将获得 intint x = * (int *) param;.

一般来说,代码构造(<i>类型</i>){<i>初始值</i>}被称为复合文字。它创建一个类型为 type 和给定初始值的对象。 & 取其地址。

复合文字具有自动存储持续时间,因此 & (int) {1} 将存在(为其保留内存)直到创建它的例程执行结束。在使用复合文字完成线程之前,您不应该让该例程结束。

如果您需要一个对象的持续时间长于将创建线程的例程的执行时间,您可以使用 malloc 为数据保留内存。