Читаю Effective Go, раздел каналы. Вот что я не понимаю, есть код:
var sem = make(chan int, MaxOutstanding)
func handle(r *Request) {
sem <- 1 // Wait for active queue to drain.
process(r) // May take a long time.
<-sem // Done; enable next request to run.
}
func Serve(queue chan *Request) {
for {
req := <-queue
go handle(req) // Don't wait for handle to finish.
}
}
Далее текст:
Данный дизайн имеет проблемы: Serve создает новую горутину для каждого входящего запроса, при этом будет запущено не более MaxOutstanding в один момент. Если количество запросов увеличивается слишком быстро, то как результат, программа может потребовать бесконечное количество ресурсов.
.
Вопрос 1 - на что может уйти бесконечное количество ресурсов, если количество горутин мы можем регулировать через параметр MaxOutstanding - на очередь запросов в заблокированный канал?
Далее:
Мы можем решить это изменением Serve используя изменения количества порождаемых горутин. Вот очевидное решение, но будьте осторожны, так как оно имеет ошибку, которую позже исправим:
func Serve(queue chan *Request) {
for req := range queue {
sem <- 1
go func() {
process(req) // Buggy; see explanation below.
<-sem
}()
}
}
Вопрос 2 - в чем принципиальная разница? В первом случае получали запрос в канал запросов, отправляли запрос в обработчик и, используя буферизированный канал как семафор для ограничения количества горутин по MaxOutstanding, запускали некий процесс, передавая в него запрос.
Во втором случае - уже без обработчика, перебираем канал запросов, присваиваем запрос в req, открываем семафор, запускаем горутину с выполнением процесса с передачей туда запроса, из горутины закрываем семафор. Не вижу, в чем должна быть разница в результате?
Вопрос 3 - апд. - уже понял, вопрос был задан по невнимательности.
Идем дальше:
Ошибка в том, что в языке Go цикл for, цикл переменной повторно используется для каждой итерации, так что переменные req разделяется по всем горутинам. Это не то что мы хотим. Нам нужно убедиться, что req является уникальной для каждой горутины. Вот один из способов, передавать значение req в качестве аргумента для закрытии горутины:
func Serve(queue chan *Request) {
for req := range queue {
sem <- 1
go func(req *Request) {
process(req)
<-sem
}(req)
}
}
Вопрос 4 - опять же, какая разница в результате?
Что за дичь в тексте с разделением req по всем горутинам? В моем понимании, получили в ходе итерации req - вызвали горутину, новый req - новая горутина, в чем проблема?
В общем, вопросов много, просьба прояснить что-как :))