Простая программа Generator-monitor с условными переменными - PullRequest
2 голосов
/ 23 ноября 2011

Я создал простую программу, которая использует условные переменные для создания синхронизации между двумя потоками.Я получаю странный вывод, который я не могу найти решение.

В потоке генератора выдает 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 ]

2 голосов
/ 23 ноября 2011

Будьте осторожны с условными переменными.Здесь есть одна большая ловушка, и это, вероятно, происходит с вами: Если вы вызываете сигнал для условной переменной, но никакой поток не ожидает ее, сигнал теряется .

Я предполагаю, что вашему главному потоку не удается дозвониться до ожидания, пока поток генератора вызовет сигнал (он может вызвать его несколько раз к тому времени, когда основной поток достигнет ожидания), и этопочему вы теряете некоторые ценности.

Отладчик не является хорошим способом проверки этого, потому что он имеет тенденцию убирать эти тонкие ошибки синхронизации.

Другие проблемы:

  1. Генератор можетЗавершить до того, как основной поток достигнет времени.
  2. Выполнено должно быть изменчивым, потому что это модифицированный перекрестный поток.
1 голос
/ 23 ноября 2011

Вам необходимо защитить все общие (глобальные) переменные мьютексами. Во-первых, это означает перемещение вызовов мьютекса в main:

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

Во-вторых, вы должны знать, что вы увидите только горстку квадратных корней, а не все, что нашел генератор. Если генератор достаточно быстрый, вы даже не увидите квадратных корней, потому что подпоток может закончить работу до того, как main увидит цикл while (done != 1).

Возможно, вы захотите использовать буфер для хранения найденных квадратных корней.

0 голосов
/ 28 ноября 2011

Все, я решил эту проблему очень легко.Мне стыдно за то, что я не понял этого раньше, но я некоторое время отдыхал от этого кода.Решение так тривиально.Все, что было необходимо, - это дополнительная переменная условия, от которой генератор ждет, пока монитор не будет завершен.Вот код.

#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);
}
0 голосов
/ 23 ноября 2011

этот код должен сработать:

#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 (), которую вы можете использовать, если ваша библиотека поддерживает ее.

Надеюсь, это помогло!

Ура, Верн

...