Golang Tutorial
Fundamentals
Control Statements
Functions & Methods
Structure
Arrays & Slices
String
Pointers
Interfaces
Concurrency
Concurrency is a key feature of Go, and goroutines play a pivotal role in implementing it. In this tutorial, we'll delve into goroutines and how they enable concurrency in Go.
Goroutines are lightweight threads managed by the Go runtime. They're much lighter than traditional operating system threads and allow you to run multiple functions concurrently. Starting a goroutine is straightforward, using the go
keyword followed by a function invocation.
go
keyword.go myFunction()
Let's take a simple example:
package main import ( "fmt" "time" ) func printNumbers() { for i := 0; i < 5; i++ { time.Sleep(250 * time.Millisecond) fmt.Println(i) } } func printLetters() { for i := 'a'; i < 'f'; i++ { time.Sleep(400 * time.Millisecond) fmt.Println(string(i)) } } func main() { go printNumbers() go printLetters() // Sleep to ensure the main function doesn't terminate immediately. time.Sleep(2 * time.Second) }
In the above program, printNumbers
and printLetters
are executed concurrently. The time.Sleep
in the main
function ensures that the program doesn't terminate immediately, giving the goroutines time to execute.
While goroutines handle concurrent execution, you often need a way to communicate or synchronize between them. This is where channels come into play.
A channel is a Go data structure that you can send values into and receive values from, providing a way for goroutines to communicate and synchronize.
ch := make(chan int)
ch <- 5 // send value to channel value := <-ch // receive value from channel
package main import "fmt" func sendData(ch chan int) { for i := 0; i < 5; i++ { ch <- i } close(ch) } func main() { dataCh := make(chan int) go sendData(dataCh) for value := range dataCh { fmt.Println(value) } }
In this example, the sendData
goroutine sends data to the dataCh
channel. The main goroutine reads data from the channel and prints it.
go
keyword to start a concurrent function is much simpler and more intuitive than dealing with thread APIs in many other languages.Goroutines are one of Go's standout features, allowing developers to write concurrent code with relative ease. Paired with channels, they provide a robust and straightforward mechanism for building highly concurrent applications. However, like all tools, they require understanding and proper usage to avoid pitfalls, such as deadlocks or race conditions.
Introduction to Go concurrency and Goroutines:
package main import ( "fmt" "time" ) func main() { go printNumbers() go printLetters() time.Sleep(time.Second) } func printNumbers() { for i := 1; i <= 5; i++ { fmt.Printf("%d ", i) time.Sleep(200 * time.Millisecond) } } func printLetters() { for char := 'a'; char < 'e'; char++ { fmt.Printf("%c ", char) time.Sleep(400 * time.Millisecond) } }
How to create and manage Goroutines in Golang:
go
keyword. Go scheduler manages these Goroutines, making it easy to work with concurrency.package main import ( "fmt" "sync" "time" ) func main() { var wg sync.WaitGroup wg.Add(2) go func() { defer wg.Done() // Your Goroutine logic here fmt.Println("Goroutine 1") }() go func() { defer wg.Done() // Your Goroutine logic here fmt.Println("Goroutine 2") }() wg.Wait() }
Concurrency patterns using Goroutines in Golang:
// Fan-out example func fanOut(input <-chan int, n int) []chan int { channels := make([]chan int, n) for i := 0; i < n; i++ { channels[i] = make(chan int) go func(ch chan int) { for value := range input { ch <- value } close(ch) }(channels[i]) } return channels }
Synchronization in Golang using Goroutines:
sync.Mutex
for synchronization.package main import ( "fmt" "sync" ) var counter = 0 var mutex sync.Mutex func main() { var wg sync.WaitGroup for i := 0; i < 1000; i++ { wg.Add(1) go incrementCounter(&wg) } wg.Wait() fmt.Println("Counter:", counter) } func incrementCounter(wg *sync.WaitGroup) { defer wg.Done() mutex.Lock() counter++ mutex.Unlock() }
Golang channels and Goroutines communication:
package main import ( "fmt" "time" ) func main() { ch := make(chan string) go sendMessage(ch) message := <-ch fmt.Println(message) } func sendMessage(ch chan string) { time.Sleep(2 * time.Second) ch <- "Hello, Goroutines!" }
Error handling in concurrent Goroutines in Golang:
package main import ( "errors" "fmt" "sync" ) func main() { var wg sync.WaitGroup errCh := make(chan error, 1) for i := 0; i < 5; i++ { wg.Add(1) go func() { defer wg.Done() if err := performTask(); err != nil { errCh <- err } }() } go func() { wg.Wait() close(errCh) }() for err := range errCh { fmt.Println("Error:", err) } } func performTask() error { // Your task logic here return errors.New("an error occurred") }