pthreads 并行化不正确的结果

pthreads parallelization incorrect result

我正在使用 pthreads 编写 C 程序。 目标是通过将给定数字作为参数传递来计算给定数字的倍数。 乘数和倍数自由选择

程序用gcc -lpthread -Wall -Wextra in.c编译,可执行文件用./a.out num amount num amount ...

调用

程序为每个“输入对”分配内存并为每个计算创建一个线程, 然后连接所有线程,并将线程写入的内存区域打印到屏幕上。

问题是程序通常至少有一个输出为空 (0x00)。 通过重复相同的输入,很少会出现正确的结果。 例如,输入 ./a.out 10 3 7 5 3 4 输出(此处压缩)如下所示:

Thread 0 result: 10 20 30  or Thread 0 result: 0 0 0    but rarely the Thread 0 result: 10 10 0 
Thread 1 result: 0 0 0 0 0 or Thread 1 result: 0 0 0 0 0   expected    Thread 1 result: 7 14 21 28 35
Thread 2 result: 3 6 9 12  or Thread 2 result: 3 6 9 12     result:    Thread 2 result: 3 6 9 12

所以我找到了两个解决方法,但都没有解决问题。它们包含在代码中但被注释掉了。

#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#define MAX_THREADS 100
int *thr_out[MAX_THREADS]; // global output record
// function to thread
void *threaded_mul(void* arguments[3])
{
  int* out = arguments[0];
  long num = (long)arguments[1];
  long len = (long)arguments[2];
  for(int i=0; i<len; i++)
    out[i]=num*(i+1);
  pthread_exit(NULL);
}

int main(int argc, char* argv[])
{
  int amt_thr = argc/2;       // one thread needs two arguments
  int thr_i_num[amt_thr];     // number to generate multiples
  int thr_o_len[amt_thr];     // how many multiples to generate
  pthread_t thr_id[amt_thr];  // holds thread ids
  long int thr_args[3];       // forms argument for pthread_create call
  printf("%d threads needed\n",amt_thr);
  for(int i=0; i<amt_thr;i++)
  { // calculate how much memory is needed for each thread
    int oi = 2*i+1; // 0 1 2 3 -> 1 3 5 7
    thr_o_len[i] = strtol(argv[oi+1], NULL, 10);
    thr_i_num[i] = strtol(argv[oi], NULL, 10);
    // allocate the memory
    thr_out[i]=calloc(thr_o_len[i], sizeof(int));
  }
  for(int i=0; i<amt_thr; i++)
  { // create threads
    thr_args[0] = (long)thr_out[i]; // address to write output to
    thr_args[1] = thr_i_num[i];     // input 'val' for thread (number to multiply)
    thr_args[2] = thr_o_len[i];     // output length 'len' for thread
    pthread_create(&thr_id[i], NULL, (void*)threaded_mul, &thr_args);
    //for(int i=0; i<32768; i++){}  //  either delay here
    //pthread_join(thr_id[i],NULL); // or wait until the thread finishes
  }
  printf("joining threads\n");
  for(int i=0; i<amt_thr; i++)
    pthread_join(thr_id[i],NULL);

   for(int t=0; t<amt_thr; t++)
  { // printing resuls
    printf("Thread %d result: ",t);
    for(int j=0; j<thr_o_len[t]; j++)
      printf("%d ",thr_out[t][j]);
    putchar('\n');
  }
  for(int i=0; i<amt_thr; i++)
    free(thr_out[i]);
  return 0;
}

我假设创建一个线程后,main正常继续并且线程立即启动(在另一个核心上)但相同的地址-space。 我的观察是,大多数时候,至少一个线程无法获得正确的参数,并且两个或多个线程执行相同的计算并写入相同的目的地, 从而使其他输出目的地保持不变。

如何避免这种行为?

编辑:据我所知,根据您的回答,问题是在新创建的线程从内存中读取其参数 &thr_args 之前,for 循环 //create threads 已经在 thr_args[] 中写了新的论点。但是参数必须是指向内存的指针,正如 pthread_create.

所要求的

Edit2:由于所述原因,我通过将所有线程的所有输入(每个线程 3 个)写入内存而不是在 for 循环内更新全局输入变量 thr_args[] 来解决问题在上面的段落中。

  1. pthread_create的原型是:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                          void *(*start_routine) (void *), void *arg);

但你传入 void *threaded_mul(void* arguments[3])。应该是:

void *threaded_mul(void* arg);

然后将 arg 转换为您需要的任何类型。

  1. threaded_mul 中,当你说你想打印一个 int 时,你传递了一个有符号的 long:
printf("%d ",num*(i+1));

应该是:

printf("%ld ",num*(i+1));
  1. main 你有:
thr_args[0] = thr_out[i];

其中 lhs 是一个 int,右侧是一个 int 指针。那可能不是你想要的。

您也许应该 post 预期的输出,但从简单阅读您的故事来看,您似乎不应该在输出中找到 zeros。

当您启动线程时,您会传入一个数组引用 (thr_args[])。这意味着每个线程都会看到相同的参数,即内存位置。你在线程创建循环中覆盖了这个数组,所以任何特定线程看到的都是时间/os/#cores 依赖。不是很随机,但是该死的 close.

作为快速破解,我将您的程序从 pthread_create 左右更改为:

 void *x = memdup(thr_args, sizeof thr_args);
 pthread_create(&thr_id[i], NULL, threaded_mul, x);

并在上面添加了一些功能:

static void *memdup(void *p, size_t n) {
        void *x;
        if ((x = malloc(n))) {
                memcpy(x, p, n);
                return x;
        } else {
                abort();
        }
}

并且您的程序打印:

Thread 0 result: 10 20 30 
Thread 1 result: 7 14 21 28 35 
Thread 2 result: 3 6 9 12 

当然,这会泄漏。所以您想更正您的程序以将参数数组与线程相关联,并在该线程的连接成功时将其删除。