Использование pthread для печати 2-го массива
Итак, у меня есть задание, которое говорит, что я создал двумерный массив [5][12] со случайными значениями от 1 до 99. Затем, используя pthreads, я должен либо добавить 1, либо вычесть 1 для каждого элемента в массиве, а затем распечатать результаты и разделить процесс на 2, 3 или 4 потока. Количество потоков зависит от того, что пользователь вводит в командной строке. У меня есть код, который компилируется и запускается. Тем не менее, мой желаемый вывод печатается только при вводе номера 3. Не могли бы вы, ребята, сказать мне, где я ошибся в своем коде? У меня проблемы с пониманием pthreads для начала.
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <ctype.h>
#include <pthread.h>
#include <iostream>
using namespace std;
int list[5][12];
int rows = 5;
int cols = 12;
int threadc;
void *threadf(void *arg)
{
int x = (int) arg;
for(int i = (x*60)/threadc; i < ((x+1) * 60)/threadc; i++)
{
for(int j = 0; j < 12; j++)
{
if (list[i][j] % 2 == 0)
list[i][j] += 1;
else
list[i][j] -= 1;
}
}
}
void cArray()
{
srand(time(NULL));
for(int i = 0; i < 5; i++)
{
for(int j = 0; j < 12; j++)
{
list[i][j] = rand() % 99 + 1;
}
}
}
void pArray(int list[][12], int rows, int cols)
{
cout << "\n";
for(int i = 0; i < rows; i++)
{
for(int j = 0; j < cols; j++)
{
cout << list[i][j] << " ";
}
cout << "\n";
}
}
int main(int argc, char *argv[])
{
if(argc != 2) exit(0);
threadc = atoi(argv[1]);
assert(threadc >= 2 && threadc <=4);
pthread_t *thread;
thread = new pthread_t [threadc];
if(thread == NULL)
exit(0);
cArray();
cout << "2-d Array: ";
pArray(list, rows, cols);
int t;
for(int i = 0; i < threadc; i++)
{
t = pthread_create(&thread[i], NULL, threadf, (void *)i);
if (t != 0)
return 1;
}
for(int i = 0; i < threadc; i++)
{
t = pthread_join(thread[i], NULL);
if(t != 0)
return 1;
}
cout << "Modified 2-d Array: ";
pArray(list, rows, cols);
return 0;
}
2 ответа
Как -то thread_join
возвращает код ошибки (22
в моем случае), когда число созданных потоков равно 2. Если вы удалите оператор возврата во втором цикле, ваша программа напечатает окончательный результат.
for(int i = 0; i < threadc; i++)
{
t = pthread_join(thread[i], NULL);
if (t != 0)
return 1; // <- your program works if you comment out this.
}
Согласно этой ссылке: http://minirighi.sourceforge.net/html/errno_8h.html22 - это EINVAL, что означает "поток не присоединяется".
Поскольку вы заботитесь о возвращаемом значении pthread_join, я бы посоветовал добавить функцию успешного завершения (pthread_exit(NULL);
в конце вашего thredf
, Кроме того, чтобы избежать переполнения буфера, как указано @user4581301, вы можете передать указатель на данные.
Так thredf
было бы как
void *threadf(void *arg)
{
cout << endl;
int x = *((int*)arg); // <- NOTICE HERE!
for(int i = (x*60)/threadc; i < ((x+1) * 60)/threadc; i++)
//...
}
pthread_exit(NULL); // <- NOTICE HERE!
}
И главное:
int main(int argc, char *argv[])
{
if(argc != 2) exit(0);
threadc = atoi(argv[1]);
assert(threadc >= 2 && threadc <=4);
pthread_t *thread;
thread = new pthread_t [threadc];
int *data = new int[threadc]; // <- NOTICE HERE!
if(thread == NULL)
exit(0);
cArray();
cout << "2-d Array: ";
pArray(list, rows, cols);
int t;
for(int i = 0; i < threadc; i++)
{
data[i] = i;
// NOTICE HERE!
t = pthread_create(&thread[i], NULL, threadf, (void *)(&data[i]));
if (t != 0)
return 1;
}
// ...
Давайте посмотрим на внешний цикл for в threadf
для x = 0 и threadc = 4
for(int i = (0*60)/4; i < ((0+1) * 60)/4; i++)
for(int i = 0; i < (1 * 60)/4; i++)
for(int i = 0; i < 60/4; i++)
for(int i = 0; i < 15; i++)
i
колеблется от 0 до 14. i используется следующим образом: list[i][j]
так что посмотрим где пишет в list[14][11]
пойдет. Хорошо вне границ, определенных int list[5][12];
Будет плохой смарф. Неопределенное поведение, поэтому технически никто не знает, что произойдет. Мы можем сделать довольно хорошие предположения, хотя.
int list[5][12];
int rows = 5; // probably overwritten by write to list[6][0]
int cols = 12; // probably overwritten by write to list[6][1]
int threadc; // probably overwritten by write to list[6][3]
Так row
а также column
движутся, но никому нет дела. Код никогда не использует их. Но threadc
... Это используется повсеместно. Фактически он используется в состоянии выхода из цикла. Больше зла может произойти здесь. Он также определяет количество потоков, которые будут созданы и объединены. Некоторые темы не могут быть созданы. Программа может попытаться присоединиться к большему количеству потоков, чем существует.
Во всяком случае, неопределенное поведение. Я полагаю, что мы все должны быть рады, что компилятор не генерировал код, который заказывал тактический ядерный удар. Так как это домашнее задание, я не собираюсь разгадывать математику. for
Цикл правильно распределить работу между несколькими потоками, но предложит рассмотреть обработку массива как массива 1D размером 5*12 и выполнение индексации 1D->2D в одном for
петля.
Другие заметки:
использование uintptr_t
вместо int
за i
в main
а также x
в threadf
, uintptr_t
гарантированно конвертировать в void *
Используйте переменную без знака, например size_t
для счетчиков цикла и индексаторов массива. Они хорошо играют с uintptr_t
и вы почти никогда не хотите отрицательный индекс массива.
Использовать std::vector
вместо указателя и new
для списка тем. Если вы должны использовать new
и указатель, не забудьте удалить список, когда вы закончите с ним.
Посмотрите, можете ли вы использовать std::thread
вместо pthread
s.
Добавить return
в threadf
,