0

我正在编写一个程序,它使用 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);
  }



  }
4

2 回答 2

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取决于kp16。现在你p16的控制很好(在互斥锁内),但你k不是,因为它是作为参数传入的。

您遇到的问题是,即使您可以在线程k == 7之前启动线程k == 8,也不能保证前者会首先获得互斥锁。如果后者首先获得互斥锁,则您的kandp16将没有“兼容”值。这就是线程的变幻莫测。

您可以通过将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

但是,我仍然认为在这种情况下线程是一个坏主意。

于 2013-01-30T02:26:49.117 回答
1

您的使用s不正确。施加的功率p16应该匹配k,但您使用的p16是随机选择的顺序功率k。这是根据我找到的公式。

之所以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;
于 2013-01-30T02:20:19.690 回答