Без реальной книги под рукой, а только с некоторыми фрагментами кода, которые кажутся неконтролируемыми, трудно сказать, что именно имел в виду автор. Но мы можем догадаться. В большинстве языков, в том числе Go, есть общий пункт о условных переменных: ожидание выполнения какого-либо условия требует всего oop в общем . В некоторых конкретных c случаях l oop не требуется.
Документация Go, я думаю, более ясна по этому поводу. В частности, текстовое описание для sync
func (c *Cond) Wait()
гласит:
Ожидание атомной разблокировки c .L и приостановка выполнения вызывающей программы. После последующего возобновления выполнения, Wait блокирует c .L перед возвратом. В отличие от других систем, Ожидание не может вернуться, если оно не вызвано Широковещанием или Сигналом.
Поскольку c .L не блокируется при первом возобновлении Ожидания , вызывающая сторона, как правило, не может предположить, что условие верно, когда Ожидание возвращается. Вместо этого вызывающий должен подождать в oop:
c.L.Lock()
for !condition() {
c.Wait()
}
... make use of condition ...
c.L.Unlock()
Я добавил жирный акцент на фразу, которая объясняет причину l oop.
Будь вы может опускать l oop зависит от более чем одной вещи:
- При каких условиях другая goroutine вызывает
Signal
и / или Broadcast
? - Как работает много подпрограмм, и что они могут делать параллельно?
Как сказано в документации Go, есть один случай, о котором не нужно беспокоиться в Go, что мы могли бы в некоторых других системах. В некоторых системах эквивалент Wait
иногда возобновляется (через эквивалент Signal
), когда Signal
(или его эквивалент) фактически не вызывается для условной переменной.
* queue
пример, который вы привели, особенно странен, потому что есть только одна процедура - та, в которой выполняется for
l oop, которая насчитывает десять, - которая может добавлять записей в очередь. Остальные goroutines только удалить записей. Таким образом, если длина очереди равна 2, и мы приостанавливаем и ожидаем сигнала об изменении длины очереди, длина очереди может измениться только на один или на ноль: никакая другая процедура не может добавить к ней и удалить из него могут только две goroutines, которые мы создали на данный момент. Это означает, что, учитывая этот конкретный пример , мы имеем один из тех случаев, когда l oop в конце концов не требуется.
(Это также странно, что queue
дано начальная емкость 10, то есть столько элементов, сколько мы поместим, а затем мы начнем ждать, когда ее длина будет ровно 2, чтобы мы не достигли этой емкости в любом случае. Если бы мы выделили дополнительные программы, которые могли бы добавить в очередь, l oop, который ожидает, пока len(queue) == 2
действительно может быть сигнализирован удалением, которое уменьшает счет со 2 до 1, но не получает шанса возобновить, пока не произойдет вставка, что увеличивает счет до 2. Однако в зависимости от ситуации, l oop может не возобновиться, пока две другие goroutines не добавят запись каждая , например, увеличив счет до 3. Так зачем повторять l oop когда длина равна точно два? Если идея состоит в том, чтобы сохранить слоты очереди, мы должны l oop, пока счетчик больше или равен 2.)
(Быть Помимо всего этого, начальная емкость не имеет значения, поскольку очередь будет динамически изменяться до большого среза, если это необходимо.)