Тебе не нужен статус c вар. Вы хотите создать новый объект (выделение памяти) каждый раз, когда вызывается semCreate
. Таким образом,
static SEM *sem ={
.val=initVal
};
должно быть
SEM *sem = malloc(sizeof(SEM));
sem->val = initVal;
Не забудьте освободить семафор, когда закончите с ним. Это включает в себя ошибки!
SEM *semCreate(int initVal){
SEM *sem = malloc(sizeof(SEM));
if (!sem)
goto Error1;
sem->val = initVal;
errno = pthread_mutex_init(&sem->m, NULL);
if (!errno)
goto Error2;
errno = pthread_cond_init(&sem->c, NULL);
if (!errno)
goto Error3;
return sem;
Error3:
pthread_mutex_destroy(&sem->m);
Error2:
free(buf);
Error1:
return NULL;
}
Помимо этого, ваш код имеет несколько проблем. Короче говоря, P
совершенно неверно.
P
может вызвать pthread_cond_signal
с разблокированным мьютексом. P
может вернуться с заблокированным мьютексом. P
уменьшает значение, когда оно не положительно, когда предполагается уменьшить его, когда оно положительно.
И есть проблема, что и P
, и V
работают бессмысленно и даже вредная обработка ошибок. Пропустить разблокировку мьютекса, если трансляция не удалась? Да, не будем этого делать.
Давайте начнем с нуля с базового c решения, без учета безопасности и эффективности.
void V(SEM *sem) {
++sem->val;
}
void P(SEM *sem) {
// Wait for the semaphore to have a positive value.
while (sem->val < 1) {
// This is where another thread could change sem->val.
}
--sem->val;
}
Теперь давайте сделаем это безопасный через взаимное исключение.
void V(SEM *sem) {
pthread_mutex_lock(&sem->m);
++sem->val;
pthread_mutex_unlock(&sem->m);
}
void P(SEM *sem) {
pthread_mutex_lock(&sem->m);
// Wait for the semaphore to have a positive value.
while (sem->val < 1) {
pthread_mutex_unlock(&sem->m);
// This is where another thread could change sem->val.
pthread_mutex_lock(&sem->m);
}
--sem->val;
pthread_mutex_unlock(&sem->m);
}
Но это напряженное ожидание. Давайте использовать условие var для сна, пока семафор не изменится. (Помните, что cond_wait
разблокирует предоставленный мьютекс при входе и блокирует его перед возвратом.)
void V(SEM *sem) {
pthread_mutex_lock(&sem->m);
++sem->val;
// Wake up a thread that's waiting, if any.
if (sem->val > 0)
pthread_cond_signal(&sem->c);
pthread_mutex_unlock(&sem->m);
}
void P(SEM *sem) {
pthread_mutex_lock(&sem->m);
// Wait for the semaphore to have a positive value.
while (sem->val < 1)
pthread_cond_wait(&sem->c, &sem->m);
--sem->val;
// Wake up a thread that's waiting, if any.
if (sem->val > 0)
pthread_cond_signal(&sem->c);
pthread_mutex_unlock(&sem->m);
}
Тада!
Примечания:
- Нет смысла вызывать
pthread_cond_broadcast
, так как только один поток может изменять семафор одновременно. Используя как V
, так и P
вызов pthread_cond_signal
, когда это уместно, мы избегаем бесполезного пробуждения потоков. - Мы можем не проверять, произошли ли сбой
pthread_mutex_lock
, pthread_mutex_unlock
и pthread_cond_signal
рабочий код, так как он не работает в результате ошибки кодирования.