Sobes.tech
Middle

WaitGroup en el núcleo utiliza Atomic para contar. WaitGroup puede aceptar el peso del trabajo. ¿Cómo reescribirías un semáforo para que los métodos Lock(peso int), TryLock(peso int) y Release(peso int) acepten peso? La goroutine captura recursos de inmediato para toda la operación, y después de cada etapa (IO-bound, CPU-bound) libera un poco. ¿Cómo lo implementarías?

sobes.tech AI

Respuesta de la IA

Для реализации семафора с весом (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 с соответствующим весом. Это позволяет гибко управлять ресурсами и избегать блокировок, если ресурсов недостаточно.