Я попытался запустить ваш код и наткнулся на несколько сюрпризов:
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*
.
person
zneak
schedule
23.02.2011