Нужен семафор, который ограничивает число одновременных обращений к общему
ресурсу: не больше n запросов к БД, не больше n открытых файлов, не больше
n параллельных скачиваний.
// New создаёт семафор на n одновременных владельцев, n >= 1.
func New(n int) *Semaphore
// Acquire занимает место, блокируясь, пока его нет.
func (s *Semaphore) Acquire()
// TryAcquire занимает место, если оно есть прямо сейчас, и не блокируется.
func (s *Semaphore) TryAcquire() bool
// Release освобождает место, занятое Acquire или TryAcquire.
func (s *Semaphore) Release()
Требования
- Одновременно место держат не больше
n горутин. При этом все n мест
реально используются: если желающих больше n, работают ровно n.
Acquire при занятых местах ждёт, пока кто-нибудь не вызовет Release.
TryAcquire возвращает false сразу, если мест нет.
Release без парного Acquire — ошибка программиста: паника, а не
зависание и не молчаливое «лишнее место».
- Код проходит
go test -race.
Пример
sem := New(3)
for _, url := range urls {
go func() {
sem.Acquire()
defer sem.Release()
download(url) // одновременно не больше 3 скачиваний
}()
}
Если клиент ушёл, держать запрос в очереди к ресурсу бессмысленно.
// AcquireContext ждёт место или отмену ctx. При ошибке место не занято.
func (s *Semaphore) AcquireContext(ctx context.Context) error
- Возвращает
nil, если место занято, и ctx.Err(), если ctx отменили или
истёк его дедлайн раньше. В этом случае место не занято, и Release
вызывать не нужно.
- Если
ctx уже отменён, возвращает ошибку, даже если свободные места есть.
Устно: как сделать взвешенный семафор, где запрос занимает k мест сразу?
Чем семафор отличается от пула воркеров?
Разбор и эталонное решение — после своей попытки: на собеседовании подсказок не будет.
Сначала попробуй сам — подсказки не дают готовый ответ.