#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. Наиболее важными являются aboutsleep
и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. Это называется условием гонки.