Кто-нибудь может объяснить как это работает? У меня есть приложение, в котором есть несколько каналов, каждый из которых обрабатывается пулом горутин. В каждый из этих каналов попадает свой тип сообщений. Всё работало хорошо, но недавно я написал вот такой метод (вкратце, он достаёт пачку необработанных сообщений stalledEvents из базы при старте приложения и рассовывает их по каналам streamChannel, которые берутся из словаря по определённому ключу).
for _, e := range stalledEvents {
streamName := e.GetStreamNameFromEventName()
streamChannel, channelExists := c.eventsChannels[streamName]
if channelExists {
streamChannel <- &e
Каждый канал - это канал указателей на события (которые каналы и должны обрабатывать) - chan *Event. В итоге, у меня началось безобразие: в каналы стали попадать события разного типа, причём возникало такое ощущение, что весь пул горутин в каждом канале пытается обработать одно событие, да ещё и не того типа, всё это ломалось на глобальном локе в Редисе, и из всей пачки stalledEvents обрабатывалась только одна запись.
Долго пытался понять в чём причина этого бардака и наткнулся на такой пассаж
https://golang.org/doc/effective_go.html#channels со слов "The bug is that in a Go for loop, the loop variable is reused for each iteration, so the req variable is shared across all goroutines." В моём случае всё пофиксилось после вот такой правки:
for _, e := range stalledEvents {
e := e
streamName := e.GetStreamNameFromEventName()
Но почему? У меня в этом методе нет анономных функций и горутин, в которых мог бы быть конфликт из-за областей видимости и перетирания переменной в цикле. Ест только складывание ссылки на структуру в цикле, но есть ещё ранее запущенные горутины, которые и читают разные типы streamChannel, причём там путаницы нет - я проверял сравнивая указатели на канал, который они слушают.