Короткий ответ: да — возможны и гонки данных, и «утечки» (бесконтрольное порождение горутин). Почему и как исправить — ниже кратко. Почему проблема: - Гонка данных/неожиданное значение: замыкание в `go func() { fmt.Println(x) }()` захватывает переменную `x`, которая переиспользуется между итерациями цикла. Поэтому печать может показать уже обновлённое значение. Это типичная ошибка (аналогично `for _, v := range ... { go func(){...v...}() }`). - Утечка/бесконечное порождение горутин: исходный цикл `for { x := <-ch; ... }` никогда не прерывается при закрытии канала — чтение из закрытого канала возвращает нулевое значение и цикл продолжит порождать горутины бесконечно. Также возможна «утечка» в виде произвольного роста числа горутин, если канал посылает данные быстрее, чем они обрабатываются. Как безопасно переписать (рекомендации и примеры): 1) Закрывать цикл по закрытию канала + передавать значение в замыкание как аргумент (надёжно решает захват и остановку): func work(ch <-chan int) { for x := range ch { // завершится при закрытии ch go func(v int) { // v копируется в момент вызова fmt.Println(v) }(x) } } 2) Если нужно ограничить число одновременно работающих горутин (чтобы не создавать их бесконечно), используйте пул воркеров: func work(ch <-chan int, workers int) { var wg sync.WaitGroup wg.Add(workers) for i := 0; i < workers; i++ { go func() { defer wg.Done() for x := range ch { fmt.Println(x) // безопасно — печать внутри воркера } }() } wg.Wait() // дождаться завершения воркеров (канал должен быть закрыт извне) } 3) Если нужно дождаться завершения всех порождённых горутин, используйте sync.WaitGroup: for x := range ch { wg.Add(1) go func(v int) { defer wg.Done() fmt.Println(v) }(x) } wg.Wait() Дополнительно: - Для отладки гонок запускайте с детектором гонок: `go run -race` / `go test -race`. - Для отмены/таймаутов удобно добавить context и завершать воркеры по сигналу. Заключение: используйте `for x := range ch` чтобы корректно завершать цикл при закрытии канала, и передавайте значение в замыкание через параметр `go func(v int){...}(x)` либо применяйте пул воркеров / WaitGroup, чтобы избежать гонок и неконтролируемого роста количества горутин.
Почему проблема:
- Гонка данных/неожиданное значение: замыкание в `go func() { fmt.Println(x) }()` захватывает переменную `x`, которая переиспользуется между итерациями цикла. Поэтому печать может показать уже обновлённое значение. Это типичная ошибка (аналогично `for _, v := range ... { go func(){...v...}() }`).
- Утечка/бесконечное порождение горутин: исходный цикл `for { x := <-ch; ... }` никогда не прерывается при закрытии канала — чтение из закрытого канала возвращает нулевое значение и цикл продолжит порождать горутины бесконечно. Также возможна «утечка» в виде произвольного роста числа горутин, если канал посылает данные быстрее, чем они обрабатываются.
Как безопасно переписать (рекомендации и примеры):
1) Закрывать цикл по закрытию канала + передавать значение в замыкание как аргумент (надёжно решает захват и остановку):
func work(ch <-chan int) {
for x := range ch { // завершится при закрытии ch
go func(v int) { // v копируется в момент вызова
fmt.Println(v)
}(x)
}
}
2) Если нужно ограничить число одновременно работающих горутин (чтобы не создавать их бесконечно), используйте пул воркеров:
func work(ch <-chan int, workers int) {
var wg sync.WaitGroup
wg.Add(workers)
for i := 0; i < workers; i++ {
go func() {
defer wg.Done()
for x := range ch {
fmt.Println(x) // безопасно — печать внутри воркера
}
}()
}
wg.Wait() // дождаться завершения воркеров (канал должен быть закрыт извне)
}
3) Если нужно дождаться завершения всех порождённых горутин, используйте sync.WaitGroup:
for x := range ch {
wg.Add(1)
go func(v int) {
defer wg.Done()
fmt.Println(v)
}(x)
}
wg.Wait()
Дополнительно:
- Для отладки гонок запускайте с детектором гонок: `go run -race` / `go test -race`.
- Для отмены/таймаутов удобно добавить context и завершать воркеры по сигналу.
Заключение: используйте `for x := range ch` чтобы корректно завершать цикл при закрытии канала, и передавайте значение в замыкание через параметр `go func(v int){...}(x)` либо применяйте пул воркеров / WaitGroup, чтобы избежать гонок и неконтролируемого роста количества горутин.