Understanding Goroutines & Channels with Ping Pong game
Deepankar Bhade /
A lesser-known fact about me is that I am a State level Table tennis player, which I played for a good 5 years. Another fun fact: Table tennis in Chinese is called "Ping Pong". But we aren't here for this BS.
"Ping Pong" in reference to networking, is communication where ping
is a transmitted packet to a destination computer, and the pong
is the response. In this blog, we will implement this ping-pong
using goroutines and channels in golang.
Let's understand Goroutines & channels briefly
Goroutines
If we run the following code we would only see the "First" function running and the code for "Second" never executes. How do we make the first function run asynchronously?
package main import ( "fmt" "time" ) func loop(msg string) { for { fmt.Println(msg) time.Sleep(time.Second) } } func main() { loop("First") loop("Second") }
Goroutines helps you create a new thread for execution and will execute concurrently upon calling. Now if we run this code we would see both of our functions running simultaneously.
package main import ( "fmt" "time" ) func loop(msg string) { for { fmt.Println(msg) time.Sleep(time.Second) } } func main() {go loop("First")loop("Second") }
Channels
Channels are the pipes that connect concurrent goroutines. You can send values into channels from one goroutine and receive those values into another goroutine. In this simple example, we create a new channel send value to the channel from a goroutine, and receive it in the main goroutine.
package main import "fmt" func main() { // creating a new channel messages := make(chan string) // sending a value into a channel go func() { messages <- "ping" }() // receiving value from the channel msg := <-messages fmt.Println(msg) }
In an ideal game of table tennis "Player A" hits the ball and "Player B" on receiving hits the ball back to "Player A".
Now let's think that Player A sends a ping
and Player B sends a pong
back and the ball is our packet. In an ideal table tennis match Player B would always wait for the ball to come i.e. ping
and respond with pong
. Player A then similarly waits for pong
and sends back Ping again.
Each player has its own goroutine since they are playing asynchronously. But in an ideal game, there still would be some sync between the players. For example, Player A needs to hit back only if Player B has returned the ball. To maintain this communication we create channels to communicate between the goroutines (players).
Player A would be "Pinger" its main job would be to wait for Ping and upon receiving send back a pong
& Player B would be "Ponger" would wait for pong
and upon receiving send back a ping
. These ping
& pong
would be the channels that would help us connect our concurrent goroutines.
Here's an implementation of pinger
and ponger
, both of these handle ping
, pong
channels as explained above.
/* Would receive from ping channel Wait for 1 second Then send to pong channel */ func pinger(ping <-chan string, pong chan<- string) { for m := range ping { printAndDelay(m) pong <- "pong" } } /* Would receive from pong channel Wait for 1 second Then send to ping channel */ func ponger(ping chan<- string, pong <-chan string) { for m := range pong { printAndDelay(m) ping <- "ping" } }
Now to start the game we need to run both pinger
& ponger
concurrently with the help of goroutines. We will also need to start the game by sending a ping
.
package main import ( "fmt" "time" ) /* Would receive from ping channel Wait for 1 second Then send to pong channel */ func pinger(ping <-chan string, pong chan<- string) { for m := range ping { printAndDelay(m) pong <- "pong" } } /* Would receive from pong channel Wait for 1 second Then send to ping channel */ func ponger(ping chan<- string, pong <-chan string) { for m := range pong { printAndDelay(m) ping <- "ping" } } func printAndDelay(msg string) { fmt.Println(msg) time.Sleep(time.Second) } func main() { ping := make(chan string) pong := make(chan string) // Player A go pinger(ping, pong) // Player B go ponger(ping, pong) // Player A starts the game ping <- "ping" for { } }
Ping Pong in action
Hope you’ve learned something new, thanks for reading!
NoteA quick favor: was anything I wrote incorrectly or misspelled, or do you still have questions? Feel free to message me on twitter .