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 {
}
}
Hope you’ve learned something new, thanks for reading!
A quick favor: was anything I wrote incorrectly or misspelled, or do you still have questions? Feel free to message me on twitter.
Top comments (2)
I think that this is a great explanation of goroutines and channels for beginners. Great Job!
Thanks means a lot to me :)