Блокирует ли pthread_cond_wait мьютекс и ложное пробуждение

TL;DR

У меня есть домашнее задание, где мне нужно реализовать синхронизацию потоков. Во время реализации я был обеспокоен тем, что 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 не выполняется и поток ложно просыпается от ожидания, проверяет условие и помечает его как не верное и выпрыгивает из цикла. Затем другой поток получает сигнал и просыпается, как обычно, блокируя мьютекс после того, как предыдущий поток уже находится в критической области. Итак, теперь оба потока окажутся в критической области, что приведет к сбою.

Вопрос

Таким образом, описанное выше может произойти только в том случае, если поток не блокирует мьютекс, когда он ложно просыпается, поэтому блокирует ли он мьютекс при ложном пробуждении или нет?

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
285
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Функция, которая может вернуться с заблокированным или разблокированным мьютексом и которая не дает вам возможности определить разницу, будет совершенно непригодной для использования. Откуда вы могли знать, разблокировать мьютекс или нет?

Функция pthread_cond_wait обеспечивает атомарную операцию «разблокировать и ждать». Это всегда повторно захватывает мьютекс перед возвратом. Если, конечно, вы не нарушаете никаких предварительных условий.

Вы можете думать об этом как о трехэтапном процессе:

  1. Атомная разблокировка и ждите.
  2. Либо другой поток сигнализирует/транслирует состояние, либо происходит ложное пробуждение.
  3. Повторно получить мьютекс

Только когда все три шага выполнены, pthread_cond_wait возвращается. Ложное пробуждение происходит, когда ожидание заканчивается без отправки сигнала другим потоком.

И это моя точка зрения, когда происходит ложное пробуждение, захватывает ли мьютекс пробуждающийся поток?

Yastanub 21.06.2019 20:07

@Yastanub Если бы это было не так, что бы вы могли сделать? Вы бы не знали, держите ли вы мьютекс или нет, поэтому не знаете, вызывать pthread_mutex_unlock или нет. Такой дефект сделал бы pthread_cond_wait полностью непригодным для использования. Также потребуется, чтобы реализация каким-то образом определяла, было ли пробуждение ложным или нет, чтобы знать, следует ли повторно захватить мьютекс, что полностью лишает смысла допускать ложные пробуждения.

David Schwartz 21.06.2019 23:44
Ответ принят как подходящий

Ваше понимание условий ложного пробуждения кажется неверным. Ложное пробуждение не имеет ничего общего с тем, заблокирован ли мьютекс другим потоком. Если у вас нет ошибки программирования (нарушение контрактов этих функций, общее повреждение памяти или другое неопределенное поведение и т. д.), pthread_cond_wait никогда не может вернуться без удерживания (блокировки) мьютекса вызывающим потоком. В случае ложного пробуждения он все равно не может вернуться, пока мьютекс не будет повторно получен. Даже в случае отмены ожидания с помощью pthread_cancel обработчик очистки отмены не может начать работу, пока мьютекс не будет повторно получен.

Спасибо, это было именно то, что я хотел знать. Я знал, что поток захватывает мьютекс на !successful! возвращается и не может ни вернуться, ни ложно проснуться, когда мьютекс запрашивается другим потоком, но я хотел знать, получает ли он также мьютекс, когда он ложно просыпается, а мьютекс все еще разблокирован.

Yastanub 21.06.2019 20:08

Другие вопросы по теме