带有条件变量的简单发电机监控程序

时间:2011-11-23 16:42:44

标签: c synchronization pthreads condition-variable

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

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

我遇到的问题很可能是某种竞争条件,因为当发电机发出信号时,显示器根本不打印出方根。

奇怪的部分是当我在每次变量is_square发生变化时逐步调试gdb b时,问题就不存在了。

任何见解都会受到赞赏。我觉得这与我的互斥或条件的放置有关。

#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 个答案:

答案 0 :(得分:2)

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

我的猜测是你的主线程无法在生成器线程调用信号的时候到达等待的调用(它可能在主线程等待的时候调用它几次),这就是为什么你是失去一些价值观。

调试器不是检查这个问题的好方法,因为它会使这些微妙的计时错误消失。

其他问题:

  1. 发生器可能在主线程到达之前完成。
  2. done应该是volatile,因为它是经过修改的跨线程。

答案 1 :(得分:1)

您需要通过互斥锁保护所有共享(全局)变量。首先,这意味着在main

中移动互斥锁调用
pthread_mutex_lock(&mutex);
while(done != 1){
   while(is_square == 0){
...
}
pthread_mutex_unlock(&mutex);

其次,你应该知道你只会看到少数平方根,而不是发电机所发现的一切。如果生成器足够快,你甚至看不到平方根,因为子线程可以在main看到while (done != 1)循环之前完成运行。

您可以考虑使用缓冲区来存储找到的平方根。

答案 2 :(得分: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;
}

你可以使用一个循环来延迟足够长的时间来达到时间片,这样你的主人就可以急于等待cond_signal()。在另一种方法中,是有另一种机制允许你的generator_func()线程等待主线程发信号。

然后,如果你的图书馆支持它,你可以使用sleep()功能。

希望它有所帮助!

干杯, 韦恩

答案 3 :(得分: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);
}