Concurrency in Go-2(Go Channels)
Tags: go, webdev, programming
Published: 2022-10-10 03:57:51

`The channel acts as a pipe by which we send typed values from one Goroutine to another. It guarantees synchronization since only one Goroutine has access to a data item at any given time. The ownership of the data is passed between different Goroutine. `
**Why use channels in golang?**

- Go channels make asynchronous programming easy and fun to use.

- Channels provide mechanisms for concurrent execution of functions to communicate by sending and receiving values of a specific element type.

**Creating a Go channel**
You only need to define a channel, channel type using the make function and assign that to a variable. Just like any other types like structs, maps and slices, you need to create a channel before you can use it.
**Syntax**

```
package main
 
func main() {
   myChannel := make(chan datatype)
}
```
**Steps to create a channel in Go**
Channels may be created using the make function. Each channel can transmit values of a specific type, called its element type. Channel types are written using the chan keyword followed by their element type.

We use to make similar to slice and map to create channels. Channels are of specific data type represented by [value-type] of the value to be shared i.e sent or received a cross goroutine. e.g ch := make(chan int).

```
ch := make(chan [value-type])
```

```
ch <- values    // sending on a channel
value := <- ch  // Receiving on a channel and assigning it to value
<-ch            // Receiving on a channel and discarding the result
close(ch)       // Closing a channel. It means that channel can't send any data

```
Example:

```
package main

import (
	"fmt"
	"os"
	"strconv"
)

// calculating total of values
func TotalValue(array []int, ch chan int) {
	total := 0
	for _, val := range array {
		total += val
	}
	// this is a sending channel
	ch <- total // send total to channel ch
}

func main() {
	ch := make(chan int)
	var err error
	val := os.Args[1:]
	nums := make([]int, len(val))
	for i := 0; i < len(val); i++ {
		if nums[i], err = strconv.Atoi(val[i]); err != nil {
			fmt.Println(err)
		}
		go TotalValue(nums, ch)
		values := <-ch // receive from ch
		fmt.Println("Total: ", values)
	}
}

```
Explanation:-
We have created a func TotalValue(array []int, ch chan int) {}that calculates totals of a given array of inputs. using the channel to send and receive values.

```
$ go run main.go 1 3 4 5 6 6 7 7 12 34 24
Total:  1
Total:  4
Total:  8
Total:  13
Total:  19
Total:  25
Total:  32
Total:  39
Total:  51
Total:  85
Total:  109
```
**Directional channel in Go**
In Go, channels are bidirectional by default, but these channels can be directional in such a way that they can only send or receive data from goroutines. This is always denoted by the arrow <- the symbol we discussed above

For example on how to create send or receive only channel
Sending channel only

```
func TotalValue(array []int, ch chan <- int) {
	total := 0
	for _, val:= range array {
		total += val
	}
	// this is a sending channel
	ch <- total // send total to channel ch
}
```
Explanation:-
ch chan <- int Using the arrow symbol indicates that our channel can only send data of type integer

`Receiving channel only`
ch <- chan int indicates that our channel can only receive data of type integer.

`Unspecified channel`
ch:= make(chan int) this channel is declared without defining direction i.e sending or receiving, thus it can either send or receive data but they are of specific data type. Mostly, they are defined in a function and can be used in any direction user needs.

**Go channel use cases**
In Go, With Go Channels we can achieve asynchronous and concurrency programming. This synchronization technique has a wider range of uses such as async/await patterns and actor models. Below are uses of the channel in Golang

`Sending/scheduling Notification`
In Go, Notification is considered as one of a kind request from a user or sending a response from the server that returns values that are not important. We use a blank struct type struct {} as the element type of notification channel since its size is zero and hence doesn't consume memory.

Example of schedule notification

```
package main

import (
	"fmt"
	"time"
)

func notification(t time.Duration) <-chan struct{} {
	ch := make(chan struct{}, 1)
	go func() {
		time.Sleep(t)
		ch <- struct{}{}
	}()
	return ch
}

func main() {
	fmt.Println("Go")
	<-notification(time.Second)
	fmt.Println("Linux")
	<-notification(time.Second)
	fmt.Println("Cloud")
}
```
Explanation:
`func notification(t time.Duration) <-chan struct{} {} mimics the After function in the time standard package. it accepts time... second, millisecond, minutes, hours, etc and it's combined with time.Sleep(duration) to ensure that the program is not blocked thus it executes concurrently.`

**Use of range in Go channels**
In Go, Range is mainly used with a for loop flow control statement to iterate over a channel to retrieve/read values in it. It maintains the FIFO principle used in queues, FIRST IN, FIRST OUT. With range, we can use different types of channels, from unbuffered to buffered.

Example use of range in a channel

```
package main

import "fmt"

func main() {
// create buffered channel
    ch := make([]chan string,3 )
   
    close(ch)

    for data := range ch {
        fmt.Println(data)
    }
}
```

**Select statement in Go channels**
In Go, the Select statement is always useful whenever numerous numbers of goroutines are sharing data. Each channel has to complete execution first before the next.
Syntax of select statement

```
select {
case <-ch1:                         // Discard received value
    fmt.Println("Got something")

case x := <-ch2:                    // Assign received value to x
    fmt.Println(x)

case ch3 <- y:                      // Send y to channel
    fmt.Println(y)

default:
    fmt.Println("None of the above")
}
```
Explanation:
ere there are three possible cases specified with three different conditions.

- If the channel ch1 is in ready to be read state, then its value will be read (and then discarded) and the text “Got something” will be printed using fmt.Println.

- If ch2 is in ready to be read state, then its value will be read and assigned to the variable x before printing the value of x.

- If ch3 is ready to be sent to, then the value y is sent to it before printing the value of y.
Finally, if no cases are ready, the default statements will be executed. If there’s no default, then the select will block until one of its cases is ready, at which point it performs the associated communication and executes the associated statements. If multiple cases are ready, select will execute one at random.

**comma ok idiom**
 In Golang, the comma ok idiom is mostly used as a for loop statement which iterates over a map[type]type{} list to retrieve values when key is passed.

The ok is true if there was a key on the map. So there are two forms of map access built into the language and two forms of this statement. I have provided a simpler code below demonstrating the map[string]string{} and use of comma ok idiom.

```
package main

import "fmt"

func main() {
	c := make(chan int)
	go func() {
		c <- 42
		close(c)
	}()

	// ok should be true as we have some content in channel
	v, ok := <-c
	fmt.Println(v, ok)

	// ok will be false as channel is empty here
	v, ok = <-c
	fmt.Println(v, ok)

}
```
**Fan in and fan out in golang channel**
In Golang, Fan In and Fan Out, works together to process data from either a single/multiple stream or pipelines into one channel mainly converge and diverging of the task. A good scenario here could be a computer processor which contains several number of tasks to be executed at simultaneously. once job done notification is shared via bus and registries are updated. Fan-In and Fan-Out works the same way. Below is an example:-

```
package main

import (
	"fmt"
	"os"
	"strconv"
)

func main() {

	tasksNo, _ := strconv.Atoi(os.Args[1])
	fmt.Printf("You entered %d number of tasks to be executed : ", tasksNo)
	//int chanel
	chanInt := make(chan int)
	//complete task channel return bool
	done := make(chan bool)

	// beffered channel of fanout no blocking
	fan_Out_Chan := make([]chan map[int]int, tasksNo)

	// Unbuffered channel which holds a limited number of tasks
	fan_In_Chan := make(chan map[int]int)

	// Random 2d array value generated
	go func(num chan<- int) {
		for i := 0; i < 10; i++ {
			for j := 0; j < 5; j++ {
				num <- j
			}
		}
		close(num)
	}(chanInt)

	for s := 0; s < tasksNo; s++ {
		fan_Out_Chan[s] = fan_out(chanInt, done, s)
	}

	// fan_In combines all results
	fan_In(done, fan_In_Chan, fan_Out_Chan...)

	go func() {
		for s := 0; s < tasksNo; s++ {
			<-done
		}
		close(fan_In_Chan)
	}()

	for key := range fan_In_Chan {
		fmt.Println(key)
	}
}

func fan_out(chan_Int  1; j-- {
				out *= j
			}
			chanOut <- map[int]int{i: out}
			intervals++
		}
		fmt.Println("Goroutine Fan_Out ", task_Name, "processed", intervals, "items")
		close(chanOut)
		done <- true
		fmt.Println("Goroutine Fan_Out", task_Name, "is finished")
	}()
	return chanOut
}

func fan_In(done chan bool, fan_In_Chan chan map[int]int, result_chan ...chan map[int]int) {
	for key, value := range result_chan {
		go func(value chan map[int]int, key int) {
			intervals := 0
			for i := range value {
				fan_In_Chan <- i
				intervals++
			}
			fmt.Println("Goroutine Fan_In  ", key, "consumed", intervals, "items")
			done <- true
		}(value, key)
	}
}
```

```
$ go run main.go 3
map[1:1]
map[3:6]
Goroutine Fan_Out  0 processed 15 items
Goroutine Fan_Out 0 is finished
Goroutine Fan_In   0 consumed 15 items
Goroutine Fan_Out  1 processed 18 items
Goroutine Fan_Out 1 is finished
map[4:24]
Goroutine Fan_Out  2 processed 17 items
Goroutine Fan_In   2 consumed 17 items
```
**context in golang channel**
`In Go, context is found everywhere in a Go program. context the package provides us with context.Context function which allows us to pass "context" across the whole program. We can pass the context provided by any function that calls it and its subroutes call that requires the context as an argument, i.efunc ReadingContext (ctx context.Context) error {}. To use context.Context effectively you need to understand how to use it and when it's needed.`

Different aspects of using context in the Go channel
- Retrieving and storing data in the database
- Passing timeout or deadline of a certain function execution
Sending cancellation signal

`Retrieving and storing data in the database`

```
package main

import (
	"context"
	"fmt"
	"github.com/gorilla/mux"
	"github.com/rs/zerolog"
	"github.com/rs/zerolog/log"
	logs "log"
	"net/http"
	"time"
)

var logKey = &struct{}{}

func requestData(w http.ResponseWriter, r *http.Request) {
	data := log.With().Str("Get_Method", r.Method).Str("original_path", r.URL.Path).Logger()
	ctx := context.WithValue(r.Context(), logKey, data)
	storeData(ctx)
}

func storeData(ctx context.Context) {
	logger := ctx.Value(logKey).(zerolog.Logger)
	logger.Debug().Msg("Data stored successfully")
}

func main() {
	fmt.Print("Server starting \n")
	router := mux.NewRouter()
	router.HandleFunc("/handle", requestData)
	srv := &http.Server{
		Handler: router,
		Addr:    "127.0.0.1:8080",
		WriteTimeout: 15 * time.Second,
		ReadTimeout:  15 * time.Second,
	}
	logs.Fatal(srv.ListenAndServe())
}
```
In Go, channels are essential for communication between goroutines. Channels support bidirectional communication i.e sending and receiving values on the same channel. This mechanism enables goroutines to synchronize without experiencing any deadlock.
                `Have a good day.`

[Concurrency in Go-1](https://dev.to/neeraj1997dev/concurrency-in-go-1-2jop)
https://dev.to/neeraj1997dev/concurrency-in-go-1-2jop