В вашем алгоритме подсчета семафора отсутствует цикл while, и он излишне сигнализирует семафор.
Исходная логика с добавленными блокировками (см. Другой ответ):
int Semaphore::P(){
Lock();
if(value <= 0){
pthread_cond_wait(&c, &m);
}
value--;
Unlock();
}
int Semaphore::V(){
Lock();
value++;
if(value > 0){
pthread_cond_signal(&c);
}
Unlock();
}
Правильный путь:
int Semaphore::P(){
Lock();
while (value <= 0){ // not if
pthread_cond_wait(&c, &m);
}
// value is now > 0, guaranteed by while loop
value--;
// value is now >= 0
Unlock();
}
int Semaphore::V(){
Lock();
int prior_value = value++;
Unlock();
// E.g. if prior_value is 50, should we signal? Why?
if (prior_value == 0) // was not signaled previously, now is.
pthread_cond_signal(&c);
}
Для эффективности собирайте информацию о том, передавать сигнал внутри мьютекса или нет, а затем передавать сигнал за пределы мьютекса.Мьютексы должны храниться на как можно меньшем количестве машинных инструкций, поскольку они добавляют конкуренцию, уменьшая параллелизм.Сигнальная операция может занимать сотни циклов (отключение ядра для манипулирования очередью ожидания).
Вы должны использовать цикл при ожидании переменной условия, поскольку возможны ложные пробуждения.Кроме того, если вы посылаете сигнал вне мьютекса, сигнал условия не всегда направляется в «предназначенный» поток.Между unlock
и signal
некоторые потоки могут проникнуть внутрь и вызвать P
и уменьшить мьютекс.Затем тот, кто пробуждается по условию, должен повторно оценить тест, иначе он будет некорректно продолжен.