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