我试图运行你的代码,遇到了几个惊喜:
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
函数与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*
返回值返回值。