Должен ли семафор ждать, а сигнал всегда вызываться из отдельных очередей?
Я просматривал правильные детали реализации семафора с использованием GCD, когда меня смутил один оператор из (https://khanlou.com/2016/04/the-GCD-handbook/): "Вызов.wait() заблокирует поток до тех пор, пока Вызывается.signal(). Это означает, что.signal() должен вызываться из другого потока, поскольку текущий поток полностью заблокирован. Кроме того, вы никогда не должны вызывать.wait() из основного потока, только из фоновых потоков." Большинство примеров семафоров обычно вызывают ожидание и сигнал из одной и той же очереди, и это тоже работает нормально. Я что-то упустил?
// 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
1 ответ
Ты спрашиваешь:
Следует ли всегда вызывать семафор ожидания и сигнал из отдельных очередей?
Семафоры всегда вызываются из отдельных потоков. Это цель семафоров - один поток отправить сигнал, которого будет ждать другой поток. Это означает, что безопасно вызывать семафоры из одной и той же параллельной очереди (поскольку индивидуально отправляемые задачи выполняются в разных рабочих потоках), но небезопасно вызывать семафоры из одной и той же последовательной очереди. Очевидно, что также безопасно вызывать семафоры из разных очередей. Главное, чтобы это были разные нити.
Вы поделились цитатой из этого документа, и все, что сказал автор, абсолютно правильно. Вызовы ожидания и сигнала должны выполняться из разных потоков. И мы никогда не хотим ждать в основном потоке какого-либо сигнала, отправленного другим, трудоемким процессом.
Затем вы сказали:
Большинство примеров семафоров обычно вызывают ожидание и сигнал из одной и той же очереди, и это тоже работает нормально. Я что-то упустил?
// 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 по своему выбору. И у нас нет возможности узнать, что он использовал. (Чтобы быть уверенным, нужно посмотреть на его реализацию.) Но вряд ли это будет та же очередь. И этот код предполагает, что это должен быть другой поток.
Весь этот шаблон, которым вы поделились с нами, является опрометчивым. Он фактически использует асинхронный метод, используя семафор, чтобы заставить его вести себя синхронно, но комментарий кода предполагает, что все это было отправлено в фоновую очередь (чтобы избежать блокировки основного потока), тем самым снова делая его асинхронным. Это немного замучено. Вам действительно нужно просто пойти дальше и вызвать этот дорогой / асинхронный метод из основного потока (что безопасно, потому что он выполняется асинхронно) и полностью потерять семафор. Может быть, автор искажал себя, чтобы проиллюстрировать, как можно использовать семафоры, но это ужасный пример.