Problem Statement
Print a series of odd-even numbers synchronously using the concept of Goroutines and Channels in Go.
Expected Output
Press Enter to Exit
1 2 3 4 5 6 7 8
Finished!
This is one of the very important questions of Golang Interviews. In this article, we will be looking at how we can write a Golang program to achieve the same.
In this blog, we discussed a simpler version of this problem where synchronisation is not a requirement and we just need to print odd even numbers using two Goroutines. If you are ready to move with our current problem, then let's continue.
Intuition
The idea is to have two Goroutine running and print the next number in the series based on the signal received from different channels i.e. printEven and printOdd. 
Let's try to understand it in detail.
Let's say we have two channels printOdd and printEven and we have two Goroutine running, let's say Goroutine A and Goroutine B.
For simplicity, let's say Goroutine A takes care of printing the odd values when a signal is received from printOdd channel and Goroutine B takes care of printing the even values when a signal is received from printEven channel.
As soon as Goroutine A receives a signal from printOdd channel, it prints an odd number, increments it by 2, and at the same time notifies the Goroutine B by sending an arbitrary value to printEven channel.
On the other hand, Goroutine B is waiting to receive a signal from printEven channel and as soon as it receives a signal, it prints an even number, increments it by 2, and at the same time notifies the Goroutine A by sending any arbitrary value to printOdd channel.

Arbitrary Value
An empty Notify struct is defined and we will use it as an arbitrary value. This struct is used as a signal to notify the goroutines when to perform certain actions.
type Notify struct {
}
since we don't want to share any other data, just notify the other Goroutine.
Goroutine A
go func() {
    start := 1
    for {
        select {
        case <-printOdd:
            time.Sleep(time.Millisecond * 500)
            fmt.Printf("%d ", start)
            start = start + 2
            printEven <- Notify{}
        case <-closer:
            return
        }
    }
}()
- Goroutine A runs an infinite loop.
- It waits for a signal on the printOddchannel. When received, it prints the current odd number, increments the counter, and signals Goroutine B to print an even number by sending an emptyNotifystruct on theprintEvenchannel.
- If a signal is received on the closerchannel, Goroutine A returns, terminating the Goroutine.
Goroutine B
go func() {
    start := 2
    for {
        select {
        case <-printEven:
            time.Sleep(time.Millisecond * 500)
            fmt.Printf("%d ", start)
            start = start + 2
            printOdd <- Notify{}
        case <-closer:
            return
        }
    }
}()
- Goroutine B runs an infinite loop.
- It waits for a signal on the printEvenchannel. When received, it prints the current even number, increments the counter, and signals Goroutine A to print an odd number by sending an emptyNotifystruct on theprintOddchannel.
- If a signal is received on the closerchannel, Goroutine B returns, terminating the Goroutine.
As you have noticed, before printing every number we are making each Goroutine sleep for 500 ms. This is done to visualize how we are printing odd-even numbers alternatively.
User Input and Execution
reader := bufio.NewReader(os.Stdin)
fmt.Println("Press Enter to cancel")
// Notify Goroutine A by sending Notify{} struct to printOdd Channel
printOdd <- Notify{}
// Wait for console input to quit
_, err := reader.ReadString('\n')
if err != nil {
    log.Fatal("Error while reading the console.")
}
fmt.Println("Finished!")
- A reader is created to read from the console.
- The message "Press Enter To Quit" is printed.
- The program triggers the initial signal by sending an empty Notifystruct on theprintOddchannel.
- The program then waits for the user to press Enter. Once Enter is pressed, it prints "Finished!" and proceeds to close the closerchannel.
Putting Everything Together
Create a odd-even-synchronisation.go file.
package main
import (
	"bufio"
	"fmt"
	"log"
	"os"
	"time"
)
// Notify
// An empty struct
type Notify struct {
}
func PrintOddEvenSynchronously() {
	// initialize all channels
	printOdd := make(chan Notify)
	printEven := make(chan Notify)
	closer := make(chan Notify)
	// spawn Goroutine A
	go func() {
		start := 1
		// An infinite loop which keeps on checking if it has received a signal
		// to print next odd number in the series and notify printEven channel.
		for {
			select {
			case <-printOdd:
				time.Sleep(time.Millisecond * 500)
				fmt.Printf("%d ", start)
				start = start + 2
				// notify Goroutine B to print an even number
				printEven <- Notify{}
			case <-closer:
				return
			}
		}
	}()
	// spawn Goroutine B
	go func() {
		start := 2
		// An infinite loop which keeps on checking if it has received a signal
		// to print next even number in the series and notify printOdd channel.
		for {
			select {
			case <-printEven:
				time.Sleep(time.Millisecond * 500)
				fmt.Printf("%d ", start)
				start = start + 2
				// notify Goroutine A to print an odd number
				printOdd <- Notify{}
			case <-closer:
				return
			}
		}
	}()
	reader := bufio.NewReader(os.Stdin)
	fmt.Println("Press Enter To Quit")
	// Notify Goroutine A by sending Notify{} struct to printOdd Channel
	printOdd <- Notify{}
	// Wait for console input to quit
	_, err := reader.ReadString('\n')
	if err != nil {
		log.Fatal("Error while reading the console.")
	}
	fmt.Println("Finished!")
	close(closer)
}
Let's create a main.go file and call the above function.
package main
func main() {
	PrintOddEvenSynchronously()
}
To test the code, fire go run . in your terminal.
Output
Press Enter To Quit
1 2 3 4 5 6 7 8 9 10 11 
Finished!
Conclusion
In this article, we have looked at how we can print the series of odd-even numbers synchronously using Goroutines and Channels in Go.
Connect with us on Discord in case you are stuck or have any questions.
