Две горутины печатают 1..N по порядку, чередуя нечётные и чётные
Реализуйте printAlternating(n), использующую ровно две горутины для печати 1..n по порядку: одна горутина печатает нечётные числа, другая — чётные, строго чередуясь. Требования: координируйте через каналы — без общего счётчика, mutex или busy-wait. Вывод должен идти строго по порядку, а функция — возвращаться только после печати всех чисел.
func printAlternating(n int) {
// ваш код здесь
}
Допишите реализацию.
Используйте два сигнальных канала как пинг-понг: горутина odd ждёт <-odd, печатает, затем сигналит even <- struct{}{}; горутина even делает зеркально. main запускает всё через odd <- struct{}{} и ждёт канал done, который even-горутина закрывает после N. Передача эстафеты обеспечивает строгий порядок без общего состояния.
- ✗Хвататься за общий счётчик или mutex вместо передачи эстафеты через канал
- ✗Считать, что один FIFO-канал заставит двух потребителей детерминированно чередоваться
- ✗Думать, что
WaitGroupупорядочивает выполнение горутин
- →Почему
struct{}{}— хорошее сигнальное значение нулевого размера на каналах? - →Как закрытие
doneиз even-горутины чисто останавливаетmain?
Задача
Двумя горутинами напечатать числа 1..N по порядку: одна печатает нечётные, другая — чётные, строго чередуясь.
func main() {
odd := make(chan struct{})
even := make(chan struct{})
done := make(chan struct{})
const N = 10
go func() { // нечётные
for i := 1; i <= N; i += 2 {
<-odd
fmt.Println("odd:", i)
even <- struct{}{}
}
}()
go func() { // чётные
for i := 2; i <= N; i += 2 {
<-even
fmt.Println("even:", i)
if i < N {
odd <- struct{}{}
} else {
close(done)
}
}
}()
odd <- struct{}{} // старт
<-done
}
Как это работает
Два небуферизированных канала odd и even работают как «пинг-понг»-эстафета. Каждая горутина блокируется, пока не получит сигнал, печатает своё число и передаёт сигнал другой:
mainзапускает цикл, отправив первый сигнал вodd;- нечётная горутина печатает и сигналит
even, чётная печатает и сигналитodd— и так далее; - после последнего числа чётная горутина закрывает
done, иmainразблокируется на<-done.
Значение struct{}{} имеет нулевой размер — это чистый сигнал, без данных. Строгий порядок обеспечивается передачей эстафеты, без общего изменяемого состояния и без блокировок.
⚠️ Классическая задача на синхронизацию через каналы, а не через разделяемую память.