В ветке entry == nil mutex не освобождается, поэтому следующий Invoke зависнет. Нужно разблокировать его перед возвратом, сохраняя сетевой вызов вне критической секции.
data[i] = &backentry{
В ветке entry == nil mutex не освобождается, поэтому следующий Invoke зависнет. Нужно разблокировать его перед возвратом, сохраняя сетевой вызов вне критической секции.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Условие
data[i] = &backentry{ backend: NewBackend(addr), inflight: 0, }
return &Balancer{ backends:data, mu: &sync.Mutex{}, }
func(b *Balancer) Invoke(ctx context.Context, req Request) (Response, error){ b.mu.Lock() entry := b.best() if entry == nil{ return nil,errors.New("no backend") } entry.inflight++ b.mu.Unlock()
defer func(){
b.mu.Lock()
entry.inflight--
b.mu.Unlock()
}()
return entry.backend.Invoke(ctx,req)
}
func(b *Balancer) best() *backentry{ var best *backentry for _,entry := b.backends{ if best == nil || entry.inflight < best.inflight{ best = entry } } return best }
Ответ
В ветке entry == nil mutex не освобождается, поэтому следующий Invoke зависнет. Нужно разблокировать его перед возвратом, сохраняя сетевой вызов вне критической секции.
Исправляемый участок:
b.mu.Lock()
entry := b.best()
if entry == nil {
b.mu.Unlock()
var zero Response
return zero, errors.New("no backend")
}
entry.inflight++
b.mu.Unlock()
Оставшийся defer уменьшает inflight после Invoke. best должен вызываться под тем же lock, а изменение списка backends — синхронизироваться. Нельзя просто отложить Unlock до конца функции: тогда сетевой вызов удержит mutex и сериализует запросы.