我正在编写一个程序,使用bailey-borwein-plouffe公式计算pi的值。我的问题是,每当我使用互斥锁运行此代码时,我会不断得到不同的结果。我不确定互斥锁是否锁定在pie函数定义应仅用于一个资源pi。
enter code here
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <time.h>
#define NUM_THREADS 1000
void *pie_function(void * p);//returns the value of pie
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; //creates a mutex variable
double pi=0,p16=1;
main()
{
pthread_t threads[NUM_THREADS]; //creates the number of threads using NUM_THREADS
int iret1; //used to ensure that threads are created properly
//pthread_create(thread,attr,start_routine,arg)
int i;
pthread_mutex_init(&mutex1, NULL);
for(i=0;i<NUM_THREADS;i++){
iret1= pthread_create(&threads[i],NULL,pie_function,(void *) i);
if(iret1){
printf("ERROR; return code from pthread_create() is %d\n", iret1);
exit(-1);
}
}
for(i=0;i<NUM_THREADS;i++){
iret1=pthread_join(threads[i],NULL);
if(iret1){
printf("ERROR; return code from pthread_create() is %d\n", iret1);
exit(-1);
}
}
pthread_mutex_destroy(&mutex1);
printf("Main: program completed. Exiting.\n");
printf("The value of pi is : %f\n",pi);
exit(0);
}
void *pie_function(void *s){
int rc;
int k=(int) s;
pthread_mutex_lock( &mutex1 ); //locks the share variable pi and p16
pi += 1.0/p16 * (4.0/(8*k + 1) - 2.0/(8*k + 4) - 1.0/(8*k + 5) - 1.0/(8*k+6));
p16 *=16;
rc=pthread_mutex_unlock( &mutex1 );
if(rc){
printf("ERROR; return code from pthread_create() is %d\n", rc);
exit(-1);
}
}
答案 0 :(得分:2)
在这种情况下,你最好完全放弃线程。
当一组线程主要在他们自己的小世界中执行而不会相互影响时,线程很方便(偶尔的资源争用是可以的)。
但是,由于你的线程非常短暂并且很好地锁定了互斥体的整个生命周期,因此线程没有任何优势。你所做的只是增加额外的开销,但收效甚微。
这项特殊任务可以更好地处理一系列连续步骤。
但是,如果您认为必须做这件事,您可以通过查看以下公式来解决问题:
pi += 1.0/p16 * (4.0/(8*k + 1) - 2.0/(8*k + 4) - 1.0/(8*k + 5) - 1.0/(8*k+6));
显然,添加到pi
的内容取决于k
和p16
。现在,您的p16
已得到良好控制(在互斥锁内)但您的k
不是因为它作为参数传入。
您遇到的问题是,即使您可以在k == 7
之前使用k == 8
启动帖子,也无法保证前者会先获得互斥锁。如果后者首先获得互斥锁,则k
和p16
将不具有“兼容”值。这就是线程的变幻莫测。
您可以通过将k
置于互斥锁的控制之下来解决此问题。
换句话说,不要将它作为参数传递,而是与p16
执行相同的操作。用以下内容初始化它们:
double pi=0, p16=1;
int k = 0;
然后让你的线程函数包含:
// Should actually check all mutex calls.
pthread_mutex_lock (&mutex1);
pi += 1.0/p16 * (4.0/(8*k + 1) - 2.0/(8*k + 4) - 1.0/(8*k + 5) - 1.0/(8*k+6));
p16 *=16;
k++;
pthread_mutex_unlock (&mutex1);
这样,无论线程执行的顺序如何,k
和p16
的值都会保持一致。
但是,在这种情况下,我仍然认为线程是个坏主意。
答案 1 :(得分:1)
您对s
的使用不正确。应用于p16
的权力应与k
匹配,但您使用p16
的连续权力随机选择k
。这是根据the formula我发现的。
随机选择k
的原因是因为一旦启动线程,您无法控制线程的顺序。
所以这一行:
int k = (int)s;
将k
设置为传入线程的值。但是哪个值k
设置为取决于线程执行的顺序。
如果带有s == 0
的帖子首先出现,并且带有s == n+1
的帖子总是在s == n
的帖子之后,你将得到正确答案。
这是我的最后一次编辑:除了完全抛弃线程之外,修复程序的一种方法是分离总和,以便每个线程计算答案的一部分,并获得正确的答案通过将每个线程的部分和加在一起。例如,您可以有4个线程,其中每个线程从i = { 0, 1, 2, 3 }
开始,每个线程计算每个第四个术语偏移量与其初始术语的总和。求和的停止条件是当计算的项低于某个阈值时。因此,pie_function
可以像这样实现:
const double THRESH = 0.0000001;
int k = (int)s;
double p16 = pow(16, k);
double p16_4 = pow(16, 4);
double subpi = 0;
for (;;) {
double k8 = 8*k;
double term = 1/p16 * (4/(k8+1) - 2/(k8+4) - 1/(k8+5) - 1/(k8+6));
if (term < THRESH) break;
subpi += term;
k += 4;
p16 *= p16_4;
}
pthread_mutex_lock(&mutex1);
pi += subpi;
pthread_mutex_unlock(&mutex1);
return 0;