Back to questions
Middle
4
WaitGroup под капотом использует Atomic для подсчёта. WaitGroup может принимать вес работы. Как бы ты переписал семафор, чтобы методы Lock(weight int), TryLock(weight int) и Release(weight int) принимали вес? Горутина захватывает ресурсы сразу на всю операцию, а после каждого стейджа (IO-bound, CPU-bound) освобождает по чуть-чуть. Как бы ты это реализовал?
Companies where asked
FinFrame
Answer from AI
sobes.tech AI
Для реализации семафора с весом (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 с соответствующим весом. Это позволяет гибко управлять ресурсами и избегать блокировок, если ресурсов недостаточно.