Ошибка в многопоточной программе

#c #ubuntu

#c #ubuntu

Вопрос:

Я должен создать программу, которая имеет общую переменную (счетчик с начальным значением = 35) и 5 потоков. Я должен сделать программу так, чтобы каждый поток обращался к значению счетчика и уменьшал его на 1. Это должно продолжаться до тех пор, пока счетчик не будет равен 0.

Это то, что у меня есть для кода до сих пор, но проблема в том, что только один поток уменьшает значение counter до 0.

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

#define NTHREADS 5
void *thread_function1(void *);
void *thread_function2(void *);
void *thread_function3(void *);
void *thread_function4(void *);
void *thread_function5(void *);
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
short int  counter = 35;


main()
{
   pthread_t thread_id[NTHREADS];
   pthread_t thread1, thread2, thread3, thread4, thread5;
int status, status2, status3, status4, status5;

status = pthread_create(amp;thread1, NULL, thread_function1, NULL);
if(status!=0){
    fprintf(stderr,"thread 1 failedn");    
}

status2 = pthread_create(amp;thread2, NULL, thread_function2, NULL);
if(status2!=0){
    fprintf(stderr,"thread 2 failedn");    
}

status3 = pthread_create(amp;thread3, NULL, thread_function3, NULL);
if(status3!=0){
    fprintf(stderr,"thread 3 failedn");    
}

status4 = pthread_create(amp;thread4, NULL, thread_function4, NULL);
if(status4!=0){
    printf("thread 4 failedn");    
}

status5 = pthread_create(amp;thread5, NULL, thread_function5, NULL);
if(status5!=0){
    fprintf(stderr,"thread 5 failedn");    
}

//pthread_join(thread1, NULL);
//int x = counter;

printf("created all the threads n");

printf("joining thread 1");
    pthread_join(thread1, NULL);
printf("joining thread 2");
    pthread_join(thread2, NULL);
printf("joining thread 3");
    pthread_join(thread3, NULL);
printf("joining thread 4");
    pthread_join(thread4, NULL);
printf("joining thread 5");
    pthread_join(thread5, NULL);                          

   printf("Final counter value: %dn", counter);
}

void *thread_function1(void *dummyPtr)
{
   printf("Thread number %ldn", pthread_self());   

while(counter>0){
   srand(time(NULL));

   int r = rand()%3;
  printf(" %dn", r);
   pthread_mutex_lock( amp;mutex1 );
  printf("entered the mutex");
   counter--;
   printf(" %dn", counter);
   sleep(r);
   pthread_mutex_unlock( amp;mutex1 );
   printf("mutex unlocked");
   pthread_yield();
}

}

void *thread_function2(void *dummyPtr)
{
   printf("Thread number %ldn", pthread_self());   

while(counter>0){
   srand(time(NULL));

   int r = rand()%3;
  printf(" %dn", r);
   pthread_mutex_lock( amp;mutex1 );
  printf("entered the mutex");
   counter--;
   printf(" %dn", counter);
   sleep(r);
   pthread_mutex_unlock( amp;mutex1 );
   pthread_yield();
}

}

void *thread_function3(void *dummyPtr)
{
   printf("Thread number %ldn", pthread_self());   

while(counter>0){
   srand(time(NULL));
   int r = rand()%3;
  printf(" %dn", r);
   pthread_mutex_lock( amp;mutex1 );
  printf("entered the mutex");
   counter--;
   printf(" %dn", counter);
   sleep(r);
   pthread_mutex_unlock( amp;mutex1 );
   pthread_yield();
}

}

void *thread_function4(void *dummyPtr)
{
   printf("Thread number %ldn", pthread_self());   

while(counter>0){
   srand(time(NULL));

   int r = rand()%3;
  printf(" %dn", r);
   pthread_mutex_lock( amp;mutex1 );
  printf("entered the mutex");
   counter--;
   printf(" %dn", counter);
   sleep(r);
   pthread_mutex_unlock( amp;mutex1 );
   pthread_yield();
}

}

void *thread_function5(void *dummyPtr)
{
   printf("Thread number %ldn", pthread_self());   

while(counter>0){
   srand(time(NULL));

   int r = rand()%3;
   printf(" %dn", r);
   pthread_mutex_lock( amp;mutex1 );
   printf("entered the mutex");
   counter--;
   printf(" %dn", counter);
   sleep(r);
   pthread_mutex_unlock( amp;mutex1 );
   pthread_yield();
}

}
  

Кто-нибудь может помочь?
Спасибо

Комментарии:

1. Почему именно у вас есть 5 идентичных потоковых функций?

2. Я делал эту часть неправильно. Теперь у меня есть только одна потоковая функция, и все потоки обращаются к этой функции.

3. Пожалуйста, запустите свой код через приличный компилятор с включенными предупреждениями -Wall или около того, прежде чем публиковать здесь. Я получаю 10 с помощью gcc. Наиболее важными являются about sleep и pthread_yield которые неизвестны. Если я затем использую правильные заголовки, он немедленно обнаружит ошибку, указанную Антти.

Ответ №1:

 int r = rand()%3;
/* ... */
sleep(rand);
  

r это случайное число от 0 до 2, но вы спите rand секунды, что является адресом функции, которая будет неявно преобразована в unsigned int — так что поток будет спать очень очень долго. Используйте sleep(r); вместо этого.

Также обратите внимание, что вы читаете counter , не удерживая мьютекс (in while(counter > 0) ), что может привести к неправильной работе программы в зависимости от архитектуры, кэширования и оптимизации компилятора. Вы должны заблокировать мьютекс, прочитать значение counter в локальной переменной, затем разблокировать мьютекс и проверить, является ли значение положительным.

Комментарии:

1. Привет, спасибо. Теперь программа запущена. Но теперь только thread1 уменьшает значение downt0 0. Есть ли что-нибудь еще, что не является правильным?

2. Как вы думаете, почему только thread1 уменьшает значение до 0?

3. Это потому, что только thread1 имеет доступ к процессору, и в результате потоки 2, хотя 5, не получают возможности для запуска. Вот почему я экспериментирую с yield и sleep, чтобы другие потоки могли работать

Ответ №2:

Хотя Антти нашел хороший вариант, у меня есть еще несколько замечаний:

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

  • вы спите с заблокированным мьютексом. Это означает, что другие потоки будут блокироваться, пока поток спит. Я думаю, вы хотите поспать после снятия блокировки, чтобы другие потоки могли иметь прилавок.

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

  • если вы последуете моему совету и переместите режим ожидания после освобождения мьютекса, то вызов pthread_yield не требуется, поскольку режим ожидания также приводит.

Комментарии:

1. Спасибо за комментарии. Теперь код выглядит намного аккуратнее. Почему рекомендуется использовать блокировку мьютекса даже во время чтения. Цель блокировки мьютекса — убедиться, что данные не повреждены, верно?

2. условие, которое у вас есть в вашем цикле while, не может быть оценено атомарно, сначала считывается счетчик, затем сравнивается. Допустим, поток # 1 считывает счетчик, и он равен 1. Затем происходит переключение на поток # 2, который также считывает счетчик, и он равен 1. Поток 2 продолжается и выполняет сравнение, входит в цикл и уменьшает счетчик до 0. Теперь поток 1 восстанавливает управление и выполняет сравнение, используя значение 1, поскольку он прочитал переменную до того, как поток # 2 уменьшил ее. И ваш счетчик снова уменьшится до -1. Это называется условием гонки.