Sharing Memory by Communicating in Golang

Using channels to share objects between goroutines

After going over code examples and explanations on sharing memory in Golang, I am putting together what I hope would be a simple demonstration of :

share memory by communicating

Lets start with some data that has to be shared between goroutines. For the sake of simplicity, I pick a slice of integers and a sum variable. Lets put it together in a struct:

type Data struct {
    list []int
    sum  int
}

I will create goroutines that will add random into numbers. Another goroutine will periodically get the sum of numbers and save to sum, and empty the numbers slice. So, a CalculateSum goroutine will save the sum of numbers slice, and empty the slice. PutNumberInList goroutine will add new random integers into 'numbers' slice. One way to avoid race condition here is to use a Mutex.

But we will use Channels, to send Data between our gouroutines. Channels are typed, so you can only send data of the type it was defined with.

ch := make(chan string)  //you can send and receive string with ch

Channels connect between our goroutines . If a goroutine is sending into channel, and there is none receiving , your goroutine waits. Similarly, when your goroutine is trying to receive from channel, and there is none sending into it, your goroutine waits. Essentially, channel send or receive is blocking unless there is someone on the other side. This helps us to synchronize the goroutines. Check out for more about channels

Now , on to our use case. We will create channel of type *Data. Then we set up our goroutines to receive from channel, modify received data, and send new data to channel. This way while one goroutine is modifying data, others are waiting to receive from channel. So our PutNumbersinList will be :

func PutNumberInList(ch chan *Data) {
    for {
        time.Sleep(3 * time.Second)  //to add numbers after every 3 seconds
        d := <-ch  //receive Data from channel 

        d.list = append(d.list, rand.Intn(10))  
        fmt.Printf("List is now : %+v\n", d.list)

        ch <- d  //send Data to channel
    }
}

And CalculateSum will be :

func CalculateSum(ch chan *Data) {
    for {
        time.Sleep(10 * time.Second)
        //Calculates sum after every 10 second
        d := <-ch //receive Data from channel

        d.sum = 0
        for _, v := range d.list {
            d.sum += v
        }
        fmt.Printf("Sum of list %+v : %d\n", d.list, d.sum)
        fmt.Printf("Emptying list \n")
        d.list = []int{}

        ch <- d //send Data to channel
    }
}

So the goroutines keep sending the same Data around , and each one modifies it, while others wait to receive.

Now putting it all together in main :

func main() {
    ch := make(chan *Data)

    newData := &Data{ //create Data that will be communicated between goroutines
        numbers: []int{},
        sum:  0,
    }
    //2 goroutines to add numbers , 1 to caluclate sum
    go PutNumberInList(inputC)
    go PutNumberInList(inputC)
    go CalculateSum(inputC)

    ch <- newData  //feeding the Data into channel so goroutines can start

    for { //ending with a lazy for loop to keep main going 
    } 

}

Thats it! Running the program gives you an ouput similar to :

List is now : [1]
List is now : [1 7]
List is now : [1 7 7]
List is now : [1 7 7 9]
Sum of list [1 7 7 9] : 24
Emptying list

This is a simple example of the idea. I hope it helps. If something can be done better in this, let me know. :)