Understanding Goroutines & Channels with Ping Pong game
Tags: go
Published: 2022-04-06 13:59:49

---
canonical_url: https://dpnkr.in/blog/goroutines-ping-pong
description: This blog explains Goroutines & Channels by implementing a Ping Pong game in Golang.
---

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?

```go
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.

```go
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.

```go
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".


![Ping Pong](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/csnetsz0gxrdi3fwuk2t.png)

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.


![Ping Pong](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/awy414ae26lzgi0mqq8n.png)

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.

```go
/*
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`.

```go
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 {
  }
}
```


![Demo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cnmp11lit4jk5bxh73a5.gif)

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](https://twitter.com/DeepankarBhade).