У меня есть домашнее задание, где мне нужно реализовать синхронизацию потоков. Во время реализации я был обеспокоен тем, что pthread_cond_wait()
также блокирует мьютекс, если он ложно просыпается, а не только если он успешно просыпается.
Задача представляет собой проблему работника/доставки, где есть рабочие потоки и потоки управления, которые предоставляют заказы для рабочих. Для этой цели существует список заказов, из которого рабочие получают заказы, а поток управления размещает заказы. Список заказов является общим объектом и требует синхронизации.
Я подумал, что для этой проблемы мне нужен монитор для списка, чтобы защитить его от обращений на пустом месте или от депозитов, когда он заполнен.
void deposit_order(order_list* ol, order* o){
pthread_mutex_lock(&(ol->mut_order_access));
while(get_count(ol) >= MAX_ORDERS) {
pthread_cond_wait(&(ol->cond_not_full), &(ol->mut_order_access));
}
ol->orders[ol->head] = o;
ol->head = (ol->head+1)%MAX_ORDERS;
ol->count++;
pthread_cond_signal(&(ol->cond_not_empty));
pthread_mutex_unlock(&(ol->mut_order_access));
}
order* get_order(order_list* ol) {
pthread_mutex_lock(&(ol->mut_order_access));
while(get_count(ol) <= 0) {
pthread_cond_wait(&(ol->cond_not_empty), &(ol->mut_order_access));
}
order* o;
o = ol->orders[ol->tail];
ol->tail = (ol->tail+1)%MAX_ORDERS;
ol->count--;
pthread_cond_signal(&(ol->cond_not_full));
pthread_mutex_unlock(&(ol->mut_order_access));
return o;
}
Я считаю, что не так важно, что содержат структуры, поскольку тема заключается в том, как синхронизировать доступ. Очевидно, что структура order_list
содержит mutex
и две условные переменные для передачи сигнала другому потоку. Таким образом, если работник удаляет задачу из списка, он сигнализирует потоку управления «не пусто», чтобы он мог внести дополнительный заказ. Если руководство размещает заказ, это сигнализирует рабочим потокам «не пустой», поэтому один поток может удалить заказ из списка.
Пока все хорошо, но теперь я думаю, что есть одно событие, когда вышеуказанное решение может быть критическим, и это «ложное пробуждение». Из эта тема я узнал, что поток не может ложно проснуться, если соответствующий мьютекс, а именно mut_order_access
, заблокирован другим потоком. Но что, если в списке есть, например, только один заказ, так что get_count(ol) >= MAX_ORDERS
не выполняется и поток ложно просыпается от ожидания, проверяет условие и помечает его как не верное и выпрыгивает из цикла. Затем другой поток получает сигнал и просыпается, как обычно, блокируя мьютекс после того, как предыдущий поток уже находится в критической области. Итак, теперь оба потока окажутся в критической области, что приведет к сбою.
Таким образом, описанное выше может произойти только в том случае, если поток не блокирует мьютекс, когда он ложно просыпается, поэтому блокирует ли он мьютекс при ложном пробуждении или нет?
Функция, которая может вернуться с заблокированным или разблокированным мьютексом и которая не дает вам возможности определить разницу, будет совершенно непригодной для использования. Откуда вы могли знать, разблокировать мьютекс или нет?
Функция pthread_cond_wait
обеспечивает атомарную операцию «разблокировать и ждать». Это всегда повторно захватывает мьютекс перед возвратом. Если, конечно, вы не нарушаете никаких предварительных условий.
Вы можете думать об этом как о трехэтапном процессе:
Только когда все три шага выполнены, pthread_cond_wait
возвращается. Ложное пробуждение происходит, когда ожидание заканчивается без отправки сигнала другим потоком.
@Yastanub Если бы это было не так, что бы вы могли сделать? Вы бы не знали, держите ли вы мьютекс или нет, поэтому не знаете, вызывать pthread_mutex_unlock
или нет. Такой дефект сделал бы pthread_cond_wait
полностью непригодным для использования. Также потребуется, чтобы реализация каким-то образом определяла, было ли пробуждение ложным или нет, чтобы знать, следует ли повторно захватить мьютекс, что полностью лишает смысла допускать ложные пробуждения.
Ваше понимание условий ложного пробуждения кажется неверным. Ложное пробуждение не имеет ничего общего с тем, заблокирован ли мьютекс другим потоком. Если у вас нет ошибки программирования (нарушение контрактов этих функций, общее повреждение памяти или другое неопределенное поведение и т. д.), pthread_cond_wait
никогда не может вернуться без удерживания (блокировки) мьютекса вызывающим потоком. В случае ложного пробуждения он все равно не может вернуться, пока мьютекс не будет повторно получен. Даже в случае отмены ожидания с помощью pthread_cancel
обработчик очистки отмены не может начать работу, пока мьютекс не будет повторно получен.
Спасибо, это было именно то, что я хотел знать. Я знал, что поток захватывает мьютекс на !successful! возвращается и не может ни вернуться, ни ложно проснуться, когда мьютекс запрашивается другим потоком, но я хотел знать, получает ли он также мьютекс, когда он ложно просыпается, а мьютекс все еще разблокирован.
И это моя точка зрения, когда происходит ложное пробуждение, захватывает ли мьютекс пробуждающийся поток?