每当我使用互斥锁同时运行此程序时,结果会有所不同

时间:2013-01-30 02:13:14

标签: c mutex

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



  }

2 个答案:

答案 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的内容取决于kp16。现在,您的p16已得到良好控制(在互斥锁内)但您的k不是因为它作为参数传入。

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

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

这样,无论线程执行的顺序如何,kp16的值都会保持一致。

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

答案 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;