Sobes.tech
Middle

WaitGroup utilise Atomic en interne pour compter. WaitGroup peut accepter un poids de travail. Comment réécrirais-tu un sémaphore pour que les méthodes Lock(poids int), TryLock(poids int) et Release(poids int) acceptent un poids ? La goroutine réserve immédiatement toutes les ressources pour l'ensemble de l'opération, puis libère un peu après chaque étape (IO-bound, CPU-bound). Comment le mettrais-tu en œuvre ?

sobes.tech IA

Réponse de l'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 с соответствующим весом. Это позволяет гибко управлять ресурсами и избегать блокировок, если ресурсов недостаточно.