【问题标题】:Invalid use of void expression with threads (C)对线程 (C) 无效使用 void 表达式
【发布时间】:2016-07-12 23:01:33
【问题描述】:

我在这里浏览了大量围绕我的问题的答案,但没有一个回答我面临的问题。

我正在用 C 编写一个多线程程序,该程序通过矩阵乘法来评估系统性能。

目前我只是尝试在传入几个变量的同时启动一个线程。

这是我的代码

pthread_t *thread = (pthread_t *) malloc(sizeof(pthread_t));

int a = malloc(sizeof(int));
int b = malloc(sizeof(int));
a = 0;
b = size;
void **args = (void **) malloc(2 * sizeof(void *));
args[0] = (void *)a;
args[1] = (void *)b;

pthread_create(&thread[0], NULL, matrixMath, &args);

还有matrixMath方法:

void* matrixMath(void* args) {
  int start = *((int *)args[0]);
  int end = *((int *)args[1]);

  printf("Start: %d, End: %d\n", start, end);

  return NULL;
}

每当我尝试编译时,我都会在“int start”和“int end”声明中得到“无效使用 void 表达式”。我以the discussion found here 为基础。有什么帮助吗?

【问题讨论】:

  • 为什么将ab 设置为malloc 返回的值,然后将它们设置为其他值,从而泄漏您分配的内存?

标签: c multithreading pthreads syntax-error void-pointers


【解决方案1】:

由于argsvoid *,您不能取消引用它,但args[0] 确实会在应用(int *) 转换之前尝试取消引用它。因此,您需要重新加上括号,以便在取消引用之前将 void * 转换为 int *

int start = ((int *)args)[0];
int end   = ((int *)args)[1];

替代(但等效):

int *array = (int *)args;  // You could skip the cast in C
int start  = args[0];
int end    = args[1];

在对pthread_create() 的调用中,我也不相信&args。这会将void *** 传递给线程代码。我认为您需要更多类似的东西,它具有多个优点,包括更简单:

pthread_t thread;
int *a = malloc(2 * sizeof(int));
a[0] = 0;
a[1] = size;

pthread_create(&thread, NULL, matrixMath, a);

你甚至可以使用:

pthread_t thread;
int a[] = { 0, size };
pthread_create(&thread, NULL, matrixMath, a);

【讨论】:

  • 我非常喜欢代码的简洁性,这确实是一种思考方式。但既然看起来很难理解,那么线程函数内部的部分或许也应该加以阐述。类似int* bound = args 然后访问bound[0]
  • 这是我一直在寻找的答案。我没有意识到括号破坏了取消引用。另外,我实际上确实有一些更接近你的第二块的东西,但我正在尝试根据我之前发布的问题让它工作。感谢您澄清这一点。
【解决方案2】:

我将演示另一种方法,使用struct。随着时间的推移,这种替代方法更易于编码和维护。

您的代码存在一些可能导致编译器和运行时错误的问题,以下是一些应该为您指明正确路径的示例:

  1. 您的int 分配没有执行您可能想要或打算执行的操作。

    您的代码将指针地址的数值分配给整数,可能由于可变大小差异而截断数据...您可能想要存储指向 int 的指针。

    你写道:

    int a = malloc(sizeof(int));
    int b = malloc(sizeof(int));
    

    你可能想写:

    int * a = malloc(sizeof(int));
    int * b = malloc(sizeof(int));
    
  2. 您的代码将void * 视为void **(或int *)而不使用任何类型转换...

    你写道:

    int start = *((int *)args[0]);
    int end   = ((int *)args)[1];
    

    你可能想写:

    int start = ( (int *)args )[0];
    int end   = ( (int *)args )[1];
    
  3. 您的代码执行 malloc 三 (3) 次,但从不调用 free(您有内存泄漏)。

如果您定义一个 struct 来包含您想要“移动”到(或与之共享)线程的数据会更容易。

例如,以下未经测试的代码可能无法正常工作,但它清楚地展示了使用struct 如何使数据传输更易于管理的概念,维护和更新。它还需要更少的调用malloc,使内存管理更容易。

struct ThreadData {
   int start;
   int end;
};

void * thread_task(void *);

int main(void) {
   struct ThreadData * data = malloc(sizeof(*data));
   data->start = 0;
   data->end = 0; // = size;
   pthread_t thr;
   pthread_create(&thr, NULL, thread_task, data);
   // ... do whatever.
   // ... remember to join thread
}

void * thread_task(void * _data) {
   struct ThreadData * data = _data;
   printf("Start: %d, End: %d\n", data->start, data->end);
   // remember to free the memory when you're done.
   free(data);
   return NULL;
}

这种方法更容易编码和维护。此外,当您需要为新线程添加数据时,这很容易 - 只需更新结构即可。

您甚至可以将复杂的返回值放回结构中的占位符中,从而允许线程执行更复杂的任务(请记住避免多个线程写入同一个 struct 字段)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-08-13
    • 2014-11-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-03
    • 1970-01-01
    相关资源
    最近更新 更多