2

我创建了一个简单的程序,它使用条件变量在两个线程之间创建同步。我得到一个奇怪的输出,我似乎找不到解决方案。

程序所做的是,在生成器线程中,生成 1000 个随机整数并检查它们是否是完全平方。如果该数字是一个完美的平方,那么它会向监视器线程发出信号,该线程打印该数字的平方根。

我遇到的问题很可能是某种竞争条件,因为当生成器发出信号时,监视器根本没有打印出平方根。

奇怪的是,当我在 gdb b 中调试时,每次变量is_square更改时单步执行,问题都不存在。

任何见解将不胜感激。我觉得这与我的互斥锁或条件放置有关。

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <pthread.h>
#include <time.h>

int square_root;
int is_square = 0;
int done = 0;
int count = 0; //used to count how many perfect squares generator_func finds
int count1 = 0; //used to compare how many responses monitor makes to signal
pthread_mutex_t mutex;
pthread_cond_t mon;


void* generator_func(void* args){
  srand(time(NULL)); 
  int i, temp, sq;
  for(i = 0; i<1000; i++){
    temp = rand() % 10000;
    sq = sqrt((double)temp);
    if((pow(sq,2)) == temp){
      pthread_mutex_lock(&mutex);
      count++;
      square_root = sq;
      is_square = 1;
      fprintf(stderr, "Square root of %d is", temp);
      pthread_cond_signal(&mon);
      pthread_mutex_unlock(&mutex);
    } 
  }
  pthread_mutex_lock(&mutex);
  done = 1;
  is_square = -1;
  pthread_cond_signal(&mon);
  pthread_mutex_unlock(&mutex);
}


main(){
  pthread_t generator; //declare thread

  pthread_mutex_init(&mutex, NULL); //initialize mutex
  pthread_cond_init(&mon, NULL); //initialize condition variable


  pthread_create(&generator, NULL, generator_func, NULL); //create thread

  //monitor
  while(done != 1){
    pthread_mutex_lock(&mutex);
    while(is_square == 0){
      pthread_cond_wait(&mon, &mutex);
    }
    if(is_square == 1 && done != 1){
      count1++;
      fprintf(stderr, " %d\n", square_root);
      is_square = 0;
    }
    pthread_mutex_unlock(&mutex);
  }

  pthread_join(generator, NULL);

  printf("%d %d\n", count, count1); //shows inconsistency between generator and monitor
  pthread_mutex_destroy(&mutex);
  pthread_cond_destroy(&mon);
} 
4

4 回答 4

2

小心条件变量。这里有一个很大的陷阱,它可能发生在您身上:如果您在条件变量上调用信号但没有线程在等待它,则信号丢失

我的猜测是,您的主线程在生成器线程调用信号时无法到达等待调用(在主线程到达等待时它可能会多次调用它),这就是您丢失一些值的原因.

调试器不是检查这一点的好方法,因为它往往会使这些微妙的时序错误消失。

其他问题:

  1. 生成器可能在主线程甚至到达 while 之前完成。
  2. done 应该是可变的,因为它是跨线程修改的。
于 2011-11-23T16:48:14.700 回答
1

You need to protect all shared (global) variables by the mutexes. Firstly, this means moving the mutex calls in main:

pthread_mutex_lock(&mutex);
while(done != 1){
   while(is_square == 0){
...
}
pthread_mutex_unlock(&mutex);

Secondly, you should be aware that you'll only see a handful of square roots, not all that the generator found. If the generator is fast enough, you'll even see no square roots, because the subthread can finish running before main sees the while (done != 1) loop.

You might consider using a buffer to store the found square roots.

于 2011-11-23T16:50:43.487 回答
0

这段代码应该可以解决问题:

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <pthread.h>
#include <time.h>

int square_root;
int is_square = 0;
int done = 0;
int count = 0; //used to count how many perfect squares generator_func finds
int count1 = 0; //used to compare how many responses monitor makes to signal
pthread_mutex_t mutex;
pthread_cond_t mon;


void* generator_func(void* args){

    //sleep(1);
    for(int j=0; j<1000; j++);  // Some delay to hit a time-slice

  srand(time(NULL)); 
  int i, temp, sq;
  for(i = 0; i<1000; i++){
    temp = rand() % 10000;
    sq = sqrt((double)temp);
    if((pow(sq,2)) == temp){
      pthread_mutex_lock(&mutex);
      count++;
      square_root = sq;
      is_square = 1;
      fprintf(stderr, "Square root of %d is", temp);
      pthread_cond_signal(&mon);
      pthread_mutex_unlock(&mutex);
    } 
  }
  pthread_mutex_lock(&mutex);
  done = 1;
  is_square = -1;
  pthread_cond_signal(&mon);
  pthread_mutex_unlock(&mutex);
}


int main(){
  pthread_t generator; //declare thread

  pthread_mutex_init(&mutex, NULL); //initialize mutex
  pthread_cond_init(&mon, NULL); //initialize condition variable


  pthread_create(&generator, NULL, generator_func, NULL); //create thread

  //monitor
    pthread_mutex_lock(&mutex);  // Protect your variables here
  while(done != 1){
    while(is_square == 0){
      pthread_cond_wait(&mon, &mutex);
    }
    if(is_square == 1 && done != 1){
      count1++;
      fprintf(stderr, " %d\n", square_root);
      is_square = 0;
    }
  }
    pthread_mutex_unlock(&mutex);

  pthread_join(generator, NULL);

  printf("%d %d\n", count, count1); //shows inconsistency between generator and monitor
  pthread_mutex_destroy(&mutex);
  pthread_cond_destroy(&mon);

return 0;
}

您可以使用一个循环来延迟足够长的时间以在之前达到一个时间片,以便您的 main 可以急于等待 cond_signal()。或者,是有另一种机制来允许您的 generator_func() 线程等待主线程发出信号。

再说一次,如果您的库支持 sleep() 功能,您可以使用它。

希望它有所帮助!

干杯,弗恩

于 2011-11-23T17:20:09.210 回答
0

所有,我已经非常非常容易地解决了这个问题。我为没有早点发现这一点而感到尴尬,但我暂时从这段代码中休息了一段时间。解决方案是如此微不足道。所需要的只是一个额外的条件变量,生成器会等待直到监视器完成。这是代码。

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <pthread.h>
#include <time.h>

int square_root;
volatile int is_square = 0;
int done = 0;
int count = 0; //used to count how many perfect squares generator_func finds
int count1 = 0; //used to compare how many responses monitor makes to signal
pthread_mutex_t mutex;
pthread_cond_t mon, gen;


void* generator_func(void* args){
  srand(time(NULL)); 
  int i, temp, sq;
  for(i = 0; i<1000; i++){
    temp = rand() % 10000;
    sq = sqrt((double)temp);
    if((pow(sq,2)) == temp){
      pthread_mutex_lock(&mutex);
      count++;
      square_root = sq;
      is_square = 1;
      fprintf(stderr, "Square root of %d is", temp);
      pthread_cond_signal(&mon); //signal monitor
      pthread_cond_wait(&gen, &mutex); //wait for monitor to finish *Solution*
      pthread_mutex_unlock(&mutex);
    } 
  }
  pthread_mutex_lock(&mutex);
  done = 1;
  is_square = -1;
  pthread_cond_signal(&mon);
  pthread_mutex_unlock(&mutex);
}


main(){
  pthread_t generator; //declare thread

  pthread_mutex_init(&mutex, NULL); //initialize mutex
  pthread_cond_init(&mon, NULL); //initialize condition var for monitor
  pthread_cond_init(&gen, NULL); //initialize condition var for generator

  pthread_create(&generator, NULL, generator_func, NULL); //create thread

  //monitor
  pthread_mutex_lock(&mutex);
  while(done != 1 || is_square == 1){
    while(is_square == 0){
      pthread_cond_wait(&mon, &mutex);
    }
    if(is_square == 1 && done != 1){
      count1++;
      fprintf(stderr, " %d\n", square_root);
      is_square = 0;
    }
    pthread_cond_signal(&gen); //start generator back up *Solution*
  }
  pthread_mutex_unlock(&mutex);


  pthread_join(generator, NULL);

  printf("%d %d\n", count, count1); //shows inconsistency between generator and monitor
  pthread_mutex_destroy(&mutex);
  pthread_cond_destroy(&mon);
}
于 2011-11-28T19:41:39.200 回答