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[]
来解决问题在上面的段落中。
- 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 转换为您需要的任何类型。
- 在
threaded_mul
中,当你说你想打印一个 int 时,你传递了一个有符号的 long:
printf("%d ",num*(i+1));
应该是:
printf("%ld ",num*(i+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
当然,这会泄漏。所以您想更正您的程序以将参数数组与线程相关联,并在该线程的连接成功时将其删除。
我正在使用 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[]
来解决问题在上面的段落中。
- 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 转换为您需要的任何类型。
- 在
threaded_mul
中,当你说你想打印一个 int 时,你传递了一个有符号的 long:
printf("%d ",num*(i+1));
应该是:
printf("%ld ",num*(i+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
当然,这会泄漏。所以您想更正您的程序以将参数数组与线程相关联,并在该线程的连接成功时将其删除。