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

Когда следует использовать семафор и когда нужно использовать условную переменную (CondVar)?

Ответ 1

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

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

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

//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() или broadcast() для пробуждения всех ожидающих потоков).

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

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

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

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

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

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

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

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

Ответ 2

Покажите, что под капотом.

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

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

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

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

  • получить мьютекс
  • проверить условие
  • блокировать и высвобождать мьютекс, если условие истинно, else release mutex

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

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

Ответ 3

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

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

Ответ 4

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

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

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

Ответ 5

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

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