Для реализации семафора с весом (weight) в методах Lock, TryLock и Release можно использовать атомарный счетчик, который отражает текущее количество занятых ресурсов, и максимальный лимит ресурсов. Горутина при захвате ресурсов пытается увеличить счетчик на вес, если после этого счетчик не превысит лимит. При освобождении ресурсов счетчик уменьшается на вес.
WaitGroup под капотом использует Atomic для подсчёта. WaitGroup может принимать вес работы. Как бы ты переписал семафор, чтобы методы Lock(weight int), TryLock(weight int) и Release(weight int) принимали вес? Горутина захватывает ресурсы сразу на всю операцию, а после каждого стейджа (IO-bound, CPU-bound) освобождает по чуть-чуть. Как бы ты это реализовал?
Для реализации семафора с весом (weight) в методах Lock, TryLock и Release можно использовать атомарный счетчик, который отражает текущее количество занятых ресурсов, и максимальный лимит ресурсов.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Для реализации семафора с весом (weight) в методах Lock, TryLock и Release можно использовать атомарный счетчик, который отражает текущее количество занятых ресурсов, и максимальный лимит ресурсов. Горутина при захвате ресурсов пытается увеличить счетчик на вес, если после этого счетчик не превысит лимит. При освобождении ресурсов счетчик уменьшается на вес.
Пример реализации на Go с использованием atomic и sync.Mutex для ожидания:
import (
"sync"
"sync/atomic"
)
type WeightedSemaphore struct {
capacity int64
current int64
mu sync.Mutex
cond *sync.Cond
}
func NewWeightedSemaphore(capacity int64) *WeightedSemaphore {
ws := &WeightedSemaphore{capacity: capacity}
ws.cond = sync.NewCond(&ws.mu)
return ws
}
func (ws *WeightedSemaphore) Lock(weight int64) {
ws.mu.Lock()
defer ws.mu.Unlock()
for {
current := atomic.LoadInt64(&ws.current)
if current+weight <= ws.capacity {
atomic.AddInt64(&ws.current, weight)
return
}
ws.cond.Wait()
}
}
func (ws *WeightedSemaphore) TryLock(weight int64) bool {
ws.mu.Lock()
defer ws.mu.Unlock()
current := atomic.LoadInt64(&ws.current)
if current+weight <= ws.capacity {
atomic.AddInt64(&ws.current, weight)
return true
}
return false
}
func (ws *WeightedSemaphore) Release(weight int64) {
ws.mu.Lock()
atomic.AddInt64(&ws.current, -weight)
ws.mu.Unlock()
ws.cond.Signal()
}
Таким образом, горутина может захватить ресурсы с определённым весом, а после каждого этапа (например, IO-bound, CPU-bound) освободить часть ресурсов, вызывая Release с соответствующим весом. Это позволяет гибко управлять ресурсами и избегать блокировок, если ресурсов недостаточно.