Условная переменная против семафора

Ответы:

207

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

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

Переменная условия обычно используется, чтобы избежать ожидания занятости (многократного цикла при проверке условия) при ожидании доступности ресурса. Например, если у вас есть поток (или несколько потоков), который не может продолжать работу до тех пор, пока очередь не станет пустой, подход к занятому ожиданию будет заключаться в том, чтобы просто сделать что-то вроде:

//pseudocode
while(!queue.empty())
{
   sleep(1);
}

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

//pseudocode
syncVar.lock.acquire();

while(!queue.empty())
{
   syncVar.wait();
}

//do stuff with queue

syncVar.lock.release();

Предположительно, у вас будет нить где-то еще, которая вытаскивает вещи из очереди. Когда очередь пуста, она может вызвать syncVar.signal()пробуждение случайного потока, который спит syncVar.wait()(или обычно есть метод signalAll()or broadcast()для пробуждения всех ожидающих потоков).

Обычно я использую такие переменные синхронизации, когда у меня есть один или несколько потоков, ожидающих одного конкретного условия (например, чтобы очередь была пустой).

Аналогичным образом можно использовать семафоры, но я думаю, что их лучше использовать, когда у вас есть общий ресурс, который может быть доступен или недоступен на основе некоторого целого числа доступных вещей. Семафоры хороши для ситуаций производитель / потребитель, когда производители распределяют ресурсы, а потребители их потребляют.

Подумайте, был ли у вас автомат по продаже газировки. Есть только один автомат с газировкой, и это общий ресурс. У вас есть один поток, который является поставщиком (производителем), который отвечает за хранение машины, и N потоков, которые являются покупателями (потребителями), которые хотят получать газированные напитки из машины. Количество содовой в машине - это целое число, которое будет управлять нашим семафором.

Каждый поток покупателя (потребителя), который приходит к автомату с газировкой, вызывает down()метод семафора, чтобы взять газировку. Это приведет к получению содовой из машины и уменьшению количества доступных газированных напитков на 1. Если есть доступные газированные напитки, код просто продолжит down()без проблем проходить мимо оператора. Если газированные напитки недоступны, поток будет спать здесь, ожидая уведомления о том, когда газированные напитки снова станут доступны (когда в машине будет больше газированных напитков).

Поток поставщика (производителя) по существу будет ждать, пока автомат с газировкой не станет пустым. Продавец получает уведомление, когда из автомата берется последняя газировка (и один или несколько потребителей потенциально ждут, чтобы достать газировку). Поставщик будет пополнять запасы газированной воды с помощью up()метода семафоров , доступное количество газированных напитков будет увеличиваться каждый раз, и, таким образом, ожидающие потоки потребителей будут уведомлены о том, что доступно больше содовой.

Эти wait()и signal()методы переменной синхронизации , как правило , должны быть скрыты внутри down()и up()операций семафора.

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

Это не обязательно самое техническое описание, но оно имеет смысл в моей голове.

Брент пишет код
источник
9
Отличный ответ, я хотел бы добавить из других ответов: семафор используется для управления количеством выполняемых потоков. Будет фиксированный набор ресурсов. Счетчик ресурсов будет уменьшаться каждый раз, когда поток владеет одним и тем же. Когда счетчик семафоров достигает 0, другим потокам не разрешается получать ресурс. Потоки блокируются до тех пор, пока другие потоки не освободят ресурсы. Короче говоря, основное различие заключается в том, сколько потоков может одновременно получать ресурс? Mutex - это ОДИН. Семафор - его DEFINED_COUNT (столько, сколько семафор)
Беркей
10
Просто чтобы уточнить, почему существует этот цикл while вместо простого if: что-то, называемое spurios wakeup . Цитата из этой статьи в Википедии : «Одна из причин этого - ложное пробуждение; то есть поток может быть разбужен из состояния ожидания, даже если ни один поток не передал сигнал переменной условия»
Владислав Бураков
3
@VladislavsBurakovs Хорошее замечание! Я думаю, что это также полезно для случая, когда широковещательная рассылка пробуждает больше потоков, чем доступно ресурсов (например, широковещательная рассылка пробуждает 3 потока, но в очереди только 2 элемента).
Брент пишет код
Я бы хотел проголосовать за ваш ответ, пока очередь не заполнится;) Идеальный ответ. Этот код может помочь определить семафоры csc.villanova.edu/~mdamian/threads/PC.htm
Mohamad-Jaafar NEHME
3
@VladislavsBurakovs Чтобы немного прояснить, причина того, что условие все еще может быть ложным для потока, который только что проснулся (что приводит к ложному пробуждению), заключается в том, что могло произойти переключение контекста до того, как поток получил возможность проверить условие опять же, когда какой-то другой запланированный поток сделал это условие ложным. Это одна из известных мне причин ложного пробуждения, но не знаю, есть ли еще.
Макс
52

Давайте раскроем, что под капотом.

Условная переменная - это, по сути, очередь ожидания , которая поддерживает операции блокировки-ожидания и пробуждения, то есть вы можете поместить поток в очередь ожидания и установить его состояние на BLOCK, а также получить поток из него и установить его состояние на READY.

Обратите внимание, что для использования условной переменной необходимы еще два элемента:

  • условие (обычно реализуется путем проверки флага или счетчика)
  • мьютекс, который защищает условие

Затем протокол становится,

  1. приобрести мьютекс
  2. проверить состояние
  3. заблокировать и освободить мьютекс, если условие истинно, иначе освободить мьютекс

Семафор - это, по сути, счетчик + мьютекс + очередь ожидания.И его можно использовать как есть без внешних зависимостей. Вы можете использовать его как мьютекс или как условную переменную.

Следовательно, семафор можно рассматривать как более сложную структуру, чем условную переменную, в то время как последняя более легкая и гибкая.

лягушка
источник
мьютекс можно рассматривать как переменную условий, это условие - удерживаться или нет.
宏杰 李
18

Семафоры можно использовать для реализации монопольного доступа к переменным, однако они предназначены для синхронизации. Мьютексы, с другой стороны, имеют семантику, которая строго связана с взаимным исключением: только процесс, заблокировавший ресурс, может разблокировать его.

К сожалению, вы не можете реализовать синхронизацию с мьютексами, поэтому у нас есть условные переменные. Также обратите внимание, что с помощью переменных условия вы можете разблокировать все ожидающие потоки в один и тот же момент, используя разблокировку широковещательной рассылки. Этого нельзя сделать с семафорами.

Дакав
источник
9

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

Барьерная синхронизация - это когда вы хотите, чтобы все ваши потоки ожидали, пока все не достигнут определенной части функции потока. это может быть реализовано с помощью статической переменной, которая изначально представляет собой значение общего числа потоков, уменьшенное каждым потоком, когда он достигает этого барьера. это будет означать, что мы хотим, чтобы каждый поток засыпал, пока не прибудет последний. Семафор будет делать прямо противоположное! с семафором каждый поток будет продолжать работать, а последний поток (который установит значение семафора на 0) перейдет в спящий режим.

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

Даниэль
источник
1

Я записываю переменные состояния при синхронизации монитора. Я обычно видел семафоры и мониторы как два разных стиля синхронизации. Между ними есть различия в том, сколько данных состояния хранится по своей сути и как вы хотите моделировать код, но на самом деле нет никаких проблем, которые можно было бы решить одним, но не другим.

Я склоняюсь к кодированию в сторону формы монитора; в большинстве языков, с которыми я работаю, все сводится к мьютексам, условным переменным и некоторым поддерживающим переменным состояния. Но семафоры тоже подойдут.

Джастин Р
источник
2
Было бы лучше, если бы вы объяснили, что такое «форма монитора».
Стивен Лу
0

Операторы mutexи conditional variablesунаследованы от semaphore.

  • Для mutex, в semaphoreиспользовании двух состояний: 0, 1
  • Для получения condition variablesв semaphore счетчике использует.

Они похожи на синтаксический сахар

Аква
источник
В библиотеке C ++ std все они являются объектами района, и все они реализованы с использованием API-интерфейсов конкретной платформы. Конечно, семафор будет разблокировать количество раз, когда сигнализируется, переменная условия может сигнализироваться несколько раз, но разблокируется только один раз. Вот почему wair принимает мьютекс в качестве параметра.
Дорон