# Goroutines and Channels in Go

## Goroutines

A goroutine is a lightweight thread of execution managed by Go's runtime. It enables concurrent programming by allowing multiple functions to run independently and simultaneously.

* Goroutines are much lighter than traditional threads.
* They share the same address space, making them efficient.
* Goroutines are started using the `go` keyword followed by a function call.

### Lightweight

Golang’s goroutines are lightweight because they are designed to be more efficient than traditional operating system (OS) threads. This efficiency comes from several optimizations at the runtime level that allow millions of goroutines to run concurrently with minimal overhead.

* Start with a small stack (about `2 KB`)
  * OS threads typically start with `2 MB`.
  * This smaller stack means less memory is allocated upfront.
* Managed by the Go runtime scheduler in user space, not by the operating system.
  * Go uses an **M:N scheduling** model, meaning **M** goroutines are multiplexed onto **N** OS threads.
  * The scheduler decides when to pause and resume goroutines.
* Faster context switching.
  * Because goroutine is user level, only a few registers and the stack pointer need to be saved.
  * OS Threads require full CPU context to switch which make it slower.
* Efficient communication using channels.

Because golang use garbage collector there is a brief pause for GC to do full scanning. For some cases this brief pause will create big impact if we running low latency application with high traffic.

### Example

```go
package main

import (
    "fmt"
    "time"
)

func printMessage(msg string) {
    for i := 0; i < 5; i++ {
        fmt.Println(msg)
        time.Sleep(100 * time.Millisecond)
    }
}

func main() {
    go printMessage("Goroutine 1") // Runs in a separate goroutine
    go printMessage("Goroutine 2") // Another goroutine
    printMessage("Main Function")  // Runs in the main thread
}
```

* The printMessage function is executed concurrently by multiple goroutines.
* The main function itself runs as a goroutine.

## Channels

A channel is a mechanism that goroutines use to communicate with each other and synchronize their execution. Channels enable the safe exchange of data between goroutines without the need for explicit locking.

* Channels are typed; you define the type of data they transmit.
* Use the `make` function to create a channel.
* Use `<-` for sending (`channel <- value`) and receiving (`value := <-channel`) data.
* Channels can be unbuffered (synchronous) or buffered (asynchronous).

### Example

```go
package main

import (
    "fmt"
    "time"
)

func sendMessage(ch chan string) {
    time.Sleep(5 * time.Second)
    ch <- "Hello from Goroutine" // Send data into the channel
}

func main() {
    ch := make(chan string) // Create a channel of string type

    go sendMessage(ch)          // Start a goroutine
    message := <-ch             // Receive data from the channel
    fmt.Println(message)        // Output: Hello from Goroutine
}
```

* The sendMessage goroutine sends data to the channel.
* The main function waits to receive the data before proceeding.

## Interaction Between Goroutines and Channels

* Sending Data: One goroutine can send data to a channel.
* Receiving Data: Another goroutine can block until it receives the data.
* Synchronization: Unbuffered channels act as synchronization points, ensuring one goroutine waits for the other.

### Example

```go
package main

import "fmt"

// Worker function to calculate sum
func calculateSum(numbers []int, result chan int) {
    sum := 0
    for _, num := range numbers {
        sum += num
    }
    result <- sum // Send result to the channel
}

func main() {
    numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

    // Create a channel to communicate results
    result := make(chan int)

    // Split work across two goroutines
    go calculateSum(numbers[:len(numbers)/2], result) // First half
    go calculateSum(numbers[len(numbers)/2:], result) // Second half

    // Receive results from both goroutines
    sum1 := <-result
    sum2 := <-result

    fmt.Println("Total Sum:", sum1+sum2) // Combine the results
}
```

* Two goroutines calculate partial sums concurrently.
* Results are sent back through the result channel.
* The main function waits to receive both `results` before combining them.

## Buffered vs. Unbuffered Channels

### Unbuffered Channels

* Default type.
* Sender blocks until a receiver is ready.
* Receiver blocks until a sender sends data.
* Ensures synchronization.

```go
ch := make(chan int) // Unbuffered channel
```

### Buffered Channels

* Allow multiple values to be queued in the channel.
* Sender only blocks when the buffer is full.
* Receiver only blocks when the buffer is empty.

```go
ch := make(chan int, 5) // Buffered channel with capacity 5
```

## Channel to Prevent Race Condition

Lets create a simplified example of data race in Golang. In here we want increase a `counter` variable, but this variable will be accessed by many go routines.

```go
var counter int

func increment() {
    for range 1_000 {
        counter++ // Race condition here
    }
}

func main() {
    for range 10 {
        go increment() // 10 goroutines incrementing concurrently
    }

    time.Sleep(1 * time.Second)
    fmt.Println("Final Counter:", counter)
}
```

```bash
➜ go run main.go
Final Counter: 6897
➜ go run main.go
Final Counter: 4626
```

As we can see above that this program returning incorrect `counter` value. This is because each goroutine modifies `counter` without synchronization, leading to data races and an incorrect value.

This can be fixed by using channel.

```go

func increment(ch chan int) {
    for range 1_000 {
        ch <- 1 // Send increment request
    }
}

func main() {
    ch := make(chan int)
    counter := 0

    go func() {
        for value := range ch {
            counter += value // Safely update counter
        }
    }()

    for range 10 {
        go increment(ch)
    }

    time.Sleep(1 * time.Second)
    close(ch)
    fmt.Println("Final Counter:", counter)
}
```

```bash
➜ go run main.go
Final Counter: 10000
➜ go run main.go
Final Counter: 10000
```

By adding channel we make sure that only one goroutine (receiver) update the counter. This will ensure safe access to the `counter` variable and return consistent correct value. This method is inline with Golangs's slogan below.

> Do not communicate by sharing memory; instead, share memory by communicating.

Please do note that the example above is oversimplified. In the real word scenario you should use `sync.WaitGroup` to wait all the goroutine to finish instead os using `time.Sleep`.

## Summary

* Goroutines provide concurrency, while channels facilitate communication and synchronization.
* Channels prevent race conditions by allowing data to flow safely between goroutines.
* Use unbuffered channels for synchronization and buffered channels for decoupled communication.

## References

* <https://go.dev/doc/effective_go>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://bagus-cahyono.gitbook.io/programming-notes/golang/goroutine_and_channel.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
