Я пишу C-программу, используя pthreads. Цель состоит в том, чтобы вычислить кратность заданных чисел, передав их в качестве аргументов. Числа для умножения и количество множителей можно свободно выбирать.
Программа компилируется с 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
продолжается нормально, и поток запускается немедленно (на другом ядре), но с тем же адресным пространством. Мое наблюдение заключается в том, что в большинстве случаев по крайней мере один поток не может получить правильные аргументы, а два или более потока выполняют одни и те же вычисления и записывают в одно и то же место назначения, оставляя, таким образом, другие места назначения вывода нетронутыми.
Как избежать такого поведения?
Редактировать: и, как я понимаю, основываясь на ваших ответах, проблема заключается в том, что до того, как вновь созданный поток сможет прочитать свои аргументы &thr_args
из памяти, цикл for
//create threads
уже записал новые аргументы в thr_args[]
. Но аргумент должен быть указателем на память, как того требует pthread_create
.
Edit2: я решил эту проблему, записав все входные данные (по 3 в каждом потоке) для всех потоков в память вместо обновления глобальной входной переменной thr_args[]
внутри цикла for
по причине, указанной в абзаце выше.
-Wall -Wextra -pedantic
и решите их все. - person Marco Bonelli   schedule 07.03.2021