Я сейчас работаю над небольшим сервисом.Из моего тестирования у кода, который я написал, есть возможность утечки подпрограмм go при определенных обстоятельствах, относящихся к контексту.Есть ли хороший и / или идиоматический способ исправить это?Ниже приведен пример кода.
func Handle(ctx context.Context, r *Req) (*Response, error) {
ctx, cancel := context.WithTimeout(ctx, time.Second * 5)
defer cancel()
resChan := make(chan Response)
errChan := make(chan error)
go process(r, resChan, errChan)
select {
case ctx.Done():
return nil, ctx.Err()
case res := <-resChan:
return &res, nil
case err := <-errChan:
return nil, err
}
}
func process(r *Req, resChan chan<- Response, errChan chan<- error) {
defer close(errChan)
defer close(resChan)
err := doSomeWork()
if err != nil {
errChan <- err
return
}
err = doSomeMoreWork()
if err != nil {
errChan <- err
return
}
res := Response{}
resChan <- res
}
Гипотетически, если клиент отменил контекст или тайм-аут произошел до того, как у функции func была возможность отправить по одному из небуферизованных каналов (resChan, errChan)от читателя не останется ни одного читателя канала, а отправка по каналам будет блокироваться бесконечно без читателей.Поскольку в этом случае процесс не вернется, каналы также не будут закрыты.
Я пришел к процессу 2 в качестве решения, но я не могу не думать, что делаю что-то не так, или естьлучший способ справиться с этим.
func process2(ctx context.Context, r *Req, resChan chan<- Response, errChan chan<- error) {
defer close(errChan)
defer close(resChan)
err := doSomeWork()
select {
case <-ctx.Done():
return
default:
if err != nil {
errChan <- err
return
}
}
err = doSomeMoreWork()
select {
case <-ctx.Done():
return
default:
if err != nil {
errChan <- err
return
}
}
res := Response{}
select{
case <-ctx.Done():
return
default:
resChan <- res
}
}
Этот подход гарантирует, что при каждой попытке отправки канала сначала проверяется контекст на предмет его завершения или отмены.Если это так, то он не пытается отправить и возвращает.Я почти уверен, что это исправляет любую утечку рутины, происходящую в первом процессе func.
Есть ли лучший способ?Может быть, я все это неправильно.