Я попытался запустить ваш код и натолкнулся на несколько неожиданностей:
printf("The number is: %d\n", finalFib);
В этой строке есть небольшая ошибка: %d
означает, что printf
ожидает int
, но передается long int
. На большинстве платформ это то же самое, или в любом случае будет иметь такое же поведение, но педантично (или если вы просто хотите остановить появление предупреждения, что также является очень благородным идеалом), вы должны использовать вместо этого %ld
, который будет ожидать long int
.
С другой стороны, ваша fib
функция выглядит неработоспособной. Тестируя его на моей машине, он не падает, но выдает 1047
, что не является числом Фибоначчи. Если присмотреться, кажется, что ваша программа неверна по нескольким аспектам:
void *fib(void *fibToFind)
{
long retval; // retval is never used
long newFibToFind = ((long)fibToFind);
long returnMinusOne; // variable is read but never initialized
long returnMinustwo; // variable is read but never initialized
pthread_t minusone; // variable is never used (?)
pthread_t minustwo; // variable is never used
if(newFibToFind == 0 || newFibToFind == 1)
// you miss a cast here (but you really shouldn't do it this way)
return newFibToFind;
else{
long newFibToFind1 = ((long)fibToFind) - 1; // variable is never used
long newFibToFind2 = ((long)fibToFind) - 2; // variable is never used
// reading undefined variables (and missing a cast)
return returnMinusOne + returnMinustwo;
}
}
Всегда заботьтесь о предупреждениях компилятора: обычно, когда вы его получаете, вы действительно делаете что-то подозрительное.
Возможно, вам следует немного пересмотреть алгоритм: сейчас ваша функция возвращает сумму двух неопределенных значений, отсюда и 1047, которое я получил ранее.
Реализация комплекта Фибоначчи с использованием рекурсивного алгоритма означает, что вам нужно снова вызвать функцию. Как отмечали другие, это довольно неэффективный способ сделать это, но это легко, поэтому я думаю, что все учителя информатики используют его в качестве примера.
Обычный рекурсивный алгоритм выглядит так:
int fibonacci(int iteration)
{
if (iteration == 0 || iteration == 1)
return 1;
return fibonacci(iteration - 1) + fibonacci(iteration - 2);
}
Я не знаю, в какой степени вы должны были использовать потоки - просто запустите алгоритм во вторичном потоке или создайте новые потоки для каждого вызова? Давайте предположим первое, поскольку оно намного проще.
Приведение целых чисел к указателям и наоборот - плохая практика, потому что если вы попытаетесь взглянуть на вещи на более высоком уровне, они должны сильно отличаться. Целые числа выполняют математические операции, а указатели разрешают адреса памяти. Это работает, потому что они представлены одинаково, но на самом деле, вы не должны этого делать. Вместо этого вы можете заметить, что функция, вызываемая для запуска вашего нового потока, принимает аргумент void*
: мы можем использовать его для передачи как , где ввод, так и , где вывод будет будет.
Итак, основываясь на моей предыдущей функции fibonacci
, вы можете использовать этот код в качестве основной подпрограммы потока:
void* fibonacci_offshored(void* pointer)
{
int* pointer_to_number = pointer;
int input = *pointer_to_number;
*pointer_to_number = fibonacci(input);
return NULL;
}
Он ожидает указатель на целое число и получает от него свой ввод, а затем записывает его там. 1 Затем вы бы создали поток, подобный этому:
int main()
{
int value = 15;
pthread_t thread;
// on input, value should contain the number of iterations;
// after the end of the function, it will contain the result of
// the fibonacci function
int result = pthread_create(&thread, NULL, fibonacci_offshored, &value);
// error checking is important! try to crash gracefully at the very least
if (result != 0)
{
perror("pthread_create");
return 1;
}
if (pthread_join(thread, NULL)
{
perror("pthread_join");
return 1;
}
// now, value contains the output of the fibonacci function
// (note that value is an int, so just %d is fine)
printf("The value is %d\n", value);
return 0;
}
Если вам нужно вызвать функцию Фибоначчи из новых отдельных потоков (обратите внимание: это не то, что я бы посоветовал, и другие, похоже, со мной согласятся; это просто взорвется за достаточно большое количество итераций), вы Сначала нужно объединить функцию fibonacci
с функцией fibonacci_offshored
. Это значительно увеличит объем, потому что работать с потоками тяжелее, чем с обычными функциями.
void* threaded_fibonacci(void* pointer)
{
int* pointer_to_number = pointer;
int input = *pointer_to_number;
if (input == 0 || input == 1)
{
*pointer_to_number = 1;
return NULL;
}
// we need one argument per thread
int minus_one_number = input - 1;
int minus_two_number = input - 2;
pthread_t minus_one;
pthread_t minus_two;
// don't forget to check! especially that in a recursive function where the
// recursion set actually grows instead of shrinking, you're bound to fail
// at some point
if (pthread_create(&minus_one, NULL, threaded_fibonacci, &minus_one_number) != 0)
{
perror("pthread_create");
*pointer_to_number = 0;
return NULL;
}
if (pthread_create(&minus_two, NULL, threaded_fibonacci, &minus_two_number) != 0)
{
perror("pthread_create");
*pointer_to_number = 0;
return NULL;
}
if (pthread_join(minus_one, NULL) != 0)
{
perror("pthread_join");
*pointer_to_number = 0;
return NULL;
}
if (pthread_join(minus_two, NULL) != 0)
{
perror("pthread_join");
*pointer_to_number = 0;
return NULL;
}
*pointer_to_number = minus_one_number + minus_two_number;
return NULL;
}
Теперь, когда у вас есть эта громоздкая функция, настройка вашей функции main
будет довольно простой: просто измените ссылку на fibonacci_offshored
на threaded_fibonacci
.
int main()
{
int value = 15;
pthread_t thread;
int result = pthread_create(&thread, NULL, threaded_fibonacci, &value);
if (result != 0)
{
perror("pthread_create");
return 1;
}
pthread_join(thread, NULL);
printf("The value is %d\n", value);
return 0;
}
Возможно, вам сказали, что потоки ускоряют параллельные процессы, но где-то есть предел, когда настройка потока обходится дороже, чем его содержимое. Это очень хороший пример такой ситуации : многопоточная версия программы работает намного, намного медленнее, чем не поточная.
В образовательных целях эта программа не работает на моем компьютере, когда число требуемых итераций равно 18, и для ее запуска требуется несколько секунд. Для сравнения, используя итеративную реализацию, мы никогда не исчерпываем потоки, и у нас есть наш ответ в течение нескольких миллисекунд. Это также значительно проще. Это было бы отличным примером того, как использование лучшего алгоритма решает многие проблемы.
Кроме того, из любопытства было бы интересно посмотреть, не падает ли он на вашей машине, и где / как.
1.Обычно вам следует избегать изменения значения переменной между ее значением на входе и ее значением после возврата из функции.Например, здесь, на входе, переменная - это количество итераций, которое мы хотим;на выходе это результат функции.Это два совершенно разных значения, и это не очень хорошая практика.Мне не хотелось использовать динамическое распределение для возврата значения через void*
возвращаемое значение.