如何通过值将结构传递给pthread?

时间:2016-09-02 21:10:49

标签: c multithreading pthreads

所以我有一个多线程的C程序,其中将创建 N pthreads。我必须通过结构给线程一些参数。为了不必分配 N 结构,检查是否没有malloc错误,通过引用我的线程传递它们然后释放结构数组,我想简单地创建一个临时结构和按值传递它。这是一个简单的代码,用于演示这个问题:

<div ></div>

我知道将值按结构传递给期望它的函数是完全正常的,但是对于线程及其NULL指针,无论我使用什么类型的转换,我都会收到错误。

4 个答案:

答案 0 :(得分:3)

确实可以通过值将结构值传递给函数。但pthread_create()期望指针作为参数(将传递给线程函数)。所以不可能通过价值传递。

我建议malloc使用N结构值并向每个线程传递一个单独的指针,然后你可以从线程函数本身中释放它们:

int main(int argc, char *argv[])
{
    int N = atoi(argv[1]);
    pthread_t *thread = malloc(N * sizeof(pthread_t));

    for (int i = 0; i < N; i++) {
        struct thread_arg arg;
        arg.value1 = i;
        arg.value2 = 'f';
        arg.value3 = i / 10.0;
        struct thread_arg *p = malloc(sizeof *p);
        *p = arg;
        pthread_create(&thread[i], NULL, foo, p);
    }

    free(thread);
    pthread_exit(NULL);
}

void *foo(void *arg)
{
    struct thread_arg my_arg = *(struct thread_arg*) arg;
    printf("%d%c%f\n", my_arg.value1, my_arg.value2, my_arg.value3);
    free(arg);
    return NULL;
}

我为了简洁而省略了错误检查。但是,您应该始终检查malloc()的返回值是否失败。

另请注意,该语句具有整数除法(i/10):

arg.value3 = i / 10;

这可能不是你想要的。您可以修复:

arg.value3 = i / 10.0;

答案 1 :(得分:2)

  

我想简单地创建一个临时结构并按值传递给[线程启动函数]。

你不能。线程启动函数接受一个void *类型的参数。您无法以任何合理的方式将结构的值转换为void *。如果void *的大小至少与结构表示的大小一样大,那么你可以解决这个问题,但对你来说情况似乎并非如此。

相反,我建议您根据需要创建一个包含多个结构的自动数组,然后将每个线程的指针传递给它自己的一个。你可以使用VLA做到这一点,即使你在运行时之前不知道你需要多少:

#define THREAD_LIMIT 50

int main(int argc, char *argv[])
{
    int N;

    if (argc < 2) {
        // handle too few arguments ...
        exit(1);
    }

    N = atoi(argv[1]);
    if (N < 0 || N > THREAD_LIMIT) {
        // handle invalid argument ...
        exit(1);
    } 

    struct thread_arg args[N];
    pthread_t threads[N];

    for (int i = 0; i < N; i++) {
        args[i].value1 = i;
        args[i].value2 = 'f';
        args[i].value3 = i / 10;

        if (pthread_create(&threads[i], NULL, foo, &args[i])) {
            // handle thread creation failure ...
        }
    }

    // did you forget to pthread_join() your threads?
    for (int i = 0; i < N; i++) {
        pthread_join(threads[i], NULL);
    }

    pthread_exit(NULL);
}

请注意,对于pthread_t数组,您可以执行相同的操作,如上所示,这使您无需手动释放内存。

如果您希望能够容纳大到可能耗尽可用堆栈空间的线程数,或者如果您需要在不支持C99的编译器上工作(例如MSVC ++),那么您可以动态分配整个arg结构数组,而不是单独分配每个结构,就像你在原始代码中对你的线程句柄一样。

无论您使用VLA还是动态分配的数组或单独的动态分配的结构,当然必须让线程在其生命周期结束后不尝试访问结构。当控制离开最里面的块封闭其声明时,VLA的生命周期结束;在上面的例子中,主线程退出时。动态分配的对象的生命周期在释放时结束,除非程序首先退出。

如果您希望任何线程在主线程退出后继续运行,因此,VLA选项不在表中,但如果您从未释放分配的内存,或者您小心,动态分配仍然可以工作在它被释放时协调。另一方面,你可以通过在退出之前连接所有子线程来轻松地保持主线程所需的时间,就像我添加到示例中一样。

顺便说一下,请注意这些方法不会消耗任何内存,而不是通过值传递结构,如果有可能这样做,因为根据定义,通过值传递结构将会复制它们

附加说明:

  • 检查程序参数,包括数字和值
  • 请检查函数调用的返回值,至少在它们有所不同的地方。因此,您应该检查pthread_create(),但在这种情况下,您可能不需要检查pthread_join()
  • 如果它确实连接了所有子线程,那么主线程可以执行普通返回或exit()或到达结束 - },而不是pthread_exit()

答案 2 :(得分:1)

正如usr所写的那样,你不能通过值将结构传递给线程启动函数,因为pthread_create的界面并不方便这样做。通常最便宜的(在逻辑复杂性和性能方面)解决方案是使用malloc并且在不再需要结构时使用结构free,但如果有某些原因你真的不想使用malloc,你可以在结构中放置一个sem_t信号量,在它完成读取之后让线程启动函数sem_post它,并让调用pthread_create的线程做sem_wait。那么&#34;父母&#34; thread控制结构的生命周期,因此它可以安全地在父线程中自动存储(&#34;在堆栈上#34;)。

答案 3 :(得分:0)

在我之前写了许多明显的答案,你不能......

... 然而 ... 有限的方式可能

如果整个结构都在sizeof(void *)范围内,则可以按值而不是参考传递该信息。

在某些系统上,主要是嵌入式和遗留系统,sizeof(void *)可能非常小(即16位)。在最近的系统中,你可以在那里获得相当多的信息(最近的系统使用64位)。

在这个例子中,我将在结构中使用32位(我将使用union代替,大多数是为了方便):

#include "stdlib.h"
#include "stdio.h"
#include "stdint.h"
#include "pthread.h"

#if !defined(UINTPTR_MAX) || UINTPTR_MAX < UINT32_MAX
#error Not enough space in a pointer for what we need or missing information.
#endif

union thread_arg {
  void *to_arg;
  struct {
    uint16_t value1;
    uint8_t value2;
    uint8_t value3;
  } data;
};

void *foo(void *arg);

int main(int argc, char *argv[]) {
  if (argc < 2)
    fprintf(stderr, "Error: Provide N\n\n"), exit(1);
  int N = atoi(argv[1]);
  if (N < 0 || N > UINT16_MAX)
    fprintf(stderr, "Error: N should be 0-%u\n\n", UINT16_MAX), exit(1);

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

  union thread_arg thread_arg;
  for (int i = 0; i < N; i++) {
    thread_arg.data.value1 = (uint16_t)i;
    thread_arg.data.value2 = 'f';
    thread_arg.data.value3 = (uint16_t)i; // whatever...?
    if (pthread_create(thread + i, NULL, foo, thread_arg.to_arg))
      fprintf(stderr, "Couldn't initiate thread no. %i\n", N), exit(1);
  }

  for (int i = 0; i < N; i++)
    pthread_join(thread[i], NULL);

  free(thread);
  pthread_exit(NULL);
}

void *foo(void *arg) {
  union thread_arg thread_arg = {.to_arg = arg};
  printf("%d%c%f\n", thread_arg.data.value1, thread_arg.data.value2,
         (float)thread_arg.data.value3 / 10);
  return NULL;
}

好锁!