Synchronization in Golang

Using channels opens up opportunities for us to synchronize between different goroutines. For example, one goroutine performs some action, the result of which is used in another goroutine. 
In this regard, we can use channels for synchronization. For example, one goroutine calculates the sum of a number, and the result is displayed in another goroutine:

package main

import "fmt"

func main() {
    chann:= make(chan int)

    go addition(23, chann)

    fmt.Println(<-chann)
}

func addition(n int, ch chan int) {
    result := 0
    for i := 1; i <= n; i++ {
        result += i
    }
    ch <- result
}

A channel does not necessarily have to carry data that represents some result on which the further execution of the goroutine depends. Sometimes this can be a dummy object, for example, an empty structure that is only needed for goroutine synchronization:

package main

import "fmt"

func main() {
    res:= make(map[int]int)
    strchann:= make(chan struct{})

    go addition(7, strchann, res)

    <-strchann// Wait for the channel to be closed

    for i, v := range res{
        fmt.Println(i, " --- ", v)
    }
}

func addition(n int, ch chan struct{}, results map[int]int) {
    defer close(ch) // Close the channel after the goroutine finishes
    result := 0
    for i := 1; i <= n; i++ {
        result += i
        results[i] = result
    }
}

In this case, the addition function calculates the cumulative sum of numbers from 1 to n and stores the results in the results map. The main function then waits for the channel to be closed and prints the results from the map.

The channel through which goroutines communicate is represented by a type chan struct{}. Moreover, the addition function does not send specific data to the channel but simply closes it after executing all its instructions using the call defer close(ch). After closing the channel in the main function, the corresponding signal is received in the line <-strchann, and after that, the main function continues its work.