
Here is the content converted into clean, structured Markdown. I have preserved the structure, code blocks, and formatting while ensuring the image placeholders are clear.

Go enables concurrency by default, and it can achieve parallelism when running on multicore CPUs.
A goroutine is a function that runs concurrently with others. Every Go program starts with one default goroutine: the main goroutine. When it finishes, all other goroutines are forcibly stopped—even if they’re still working.
Goroutines are lightweight execution threads, created and managed by the Go runtime—not the operating system. This makes them cheaper and faster to work with than traditional OS threads.
| Aspect | Goroutines | Threads |
|---|---|---|
| Management | Go runtime | OS |
| Memory Usage | Few KBs | Few MBs |
| Creation cost | Extremely low | High |
| Scalability | High (millions) | Low (limited by OS) |
The syntax of goroutines couldn’t be simpler:
go sayHello()
Here’s a basic example:
package main
import (
"fmt"
"time"
)
func sayHello() {
fmt.Println("Hello from goroutine!")
}
func main() {
go sayHello()
time.Sleep(time.Second)
}
Why the Sleep? Because the main goroutine may finish before the sayHello() goroutine executes. In real applications, we use sync.WaitGroup instead:
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var wg sync.WaitGroup
sayHello("Main")
wg.Add(1)
go func() {
defer wg.Done()
sayHello("Goroutine")
}()
wg.Wait() // Wait for the goroutine to finish
}
func sayHello(s string) {
for i := 0; i < 3; i++ {
fmt.Printf("Hello %s\n", s)
time.Sleep(time.Second)
}
}
A channel is a typed conduit for communication between goroutines. It allows them to send and receive data safely and concurrently without explicit locking. By default, channels are bidirectional, meaning they can be used to both send and receive data.
"Do not communicate by sharing memory; instead, share memory by communicating." — Rob Pike
Declaration using chan:
var channelName chan Type
// or
channelName := make(chan Type)
To send and receive data through a channel, we use the <- operator. This operator acts like a directional indicator, showing where the data is flowing.
channelName <- valuemyVar := <- channelNameExample:
package main
import "fmt"
func main() {
myChan := make(chan int)
go func() {
myChan <- 1
}()
fmt.Println(<-myChan)
}
One powerful aspect of channels is that send/receive operations block until the other side is ready. This allows goroutines to communicate in a synchronized way—without locks.

package main
func main() {
myChan := make(chan int)
myChan <- 1 // deadlock here: no one is receiving
fmt.Println(<-myChan)
}
package main
import "fmt"
func main() {
myChan := make(chan int)
go func() {
myChan <- 1
}()
fmt.Println(<-myChan)
}
Go is widely adopted in cloud-native development (e.g., Docker, Kubernetes) because:
Go makes concurrency simple yet powerful with its goroutines and channels. By abstracting complex thread management behind a clean syntax, Go empowers developers to write efficient, scalable, and readable concurrent code with ease. Mastering Go’s concurrency model is a must-have skill for modern backend development.