Вы спросите:
Должен ли семафор ожидания и сигнал всегда вызываться из отдельных очередей?
Семафоры всегда вызываются из отдельных потоков. Это предназначение семафоров, чтобы один поток отправил сигнал, которого будет ждать другой поток. Это означает, что безопасно вызывать семафоры из одной и той же параллельной очереди (поскольку индивидуально отправляемые задачи выполняются в разных рабочих потоках), но небезопасно вызывать семафоры из одной последовательной очереди. Очевидно, что также безопасно вызывать семафоры из разных очередей. Главное, чтобы это были разные нити.
Вы поделились цитатой из этого документа, и все, что сказал автор, абсолютно правильно. Вызовы ожидания и сигнала должны выполняться из разных потоков. И мы никогда не хотим ждать в основном потоке какого-либо сигнала, отправленного другим, трудоемким процессом.
Затем вы сказали:
Большинство примеров семафора обычно вызывают ожидание и сигнал из одной и той же очереди, и это тоже работает нормально. Я что-то упустил?
// Pseudocode from: https://khanlou.com/2016/04/the-GCD-handbook/
// on a background queue
let semaphore = DispatchSemaphore(value: 0)
doSomeExpensiveWorkAsynchronously(completionBlock: {
semaphore.signal()
})
semaphore.wait()
//the expensive asynchronous work is now done
Несколько наблюдений:
Этот шаблон работает, только если signal
и wait
находятся в разных потоках . Если бы они были одним потоком, это было бы тупиком. Итак, очевидно, что автор предполагает, что они находятся в разных темах.
Кажется, вы подразумеваете, что эти два вызова находятся «в одной очереди». Это неверное предположение (и, откровенно говоря, маловероятное). Чтобы убедиться, нам нужно увидеть реализацию этого «дорогостоящего асинхронного» метода. Но когда вы видите подобное закрытие, это обычно означает, что метод отправил это закрытие в некоторую очередь GCD по своему выбору. И у нас нет возможности узнать, что он использовал. (Чтобы быть уверенным, нужно посмотреть на его реализацию.) Но вряд ли это будет та же очередь. И этот код предполагает, что это должен быть другой поток.
Весь этот шаблон, которым вы поделились с нами, является опрометчивым. Он фактически использует асинхронный метод, используя семафор, чтобы заставить его вести себя синхронно, но комментарий кода предполагает, что все это было отправлено в фоновую очередь (чтобы избежать блокировки основного потока), тем самым снова делая его асинхронным. Это немного замучено. Вам действительно нужно просто go вперед и вызвать этот дорогой / асинхронный метод из основного потока (что безопасно, потому что он выполняется асинхронно) и полностью потерять семафор. Может быть, автор искажал себя, чтобы проиллюстрировать, как можно использовать семафоры, но это ужасный пример.