Concurrency in Go

Introduction to Concurrency

Concurrency is the ability of a program to execute multiple tasks at the same time. Go provides built-in support for concurrency, making it efficient and easy to use.

In this article, we will explore the key concurrency features in Go, including goroutines, channels, buffered channels, and the `select` statement.

Goroutines

A goroutine is a lightweight thread managed by the Go runtime. It allows functions to run concurrently, improving performance and responsiveness.

package main

import (
    "fmt"
    "time"
)

func hello() {
    fmt.Println("Hello from goroutine!")
}

func main() {
    go hello()
    time.Sleep(time.Second) // Allow goroutine to complete
    fmt.Println("Main function execution")
}

Here, go hello() starts a new goroutine to execute the hello function asynchronously.

Channels

Channels are used to communicate between goroutines safely and efficiently. They help synchronize operations and avoid race conditions.

package main

import "fmt"

func main() {
    messages := make(chan string)
    
    go func() {
        messages <- "Hello, Go Concurrency!"
    }()
    
    msg := <-messages
    fmt.Println(msg)
}

In this example, a goroutine sends a message into a channel, and the main function receives and prints it.

Buffered Channels

Buffered channels allow multiple values to be stored before they are received, helping to manage workload efficiently.

package main

import "fmt"

func main() {
    messages := make(chan string, 2)
    messages <- "Hello"
    messages <- "World"
    fmt.Println(<-messages)
    fmt.Println(<-messages)
}

The buffer size determines how many values can be stored before blocking. In this example, two messages are stored and retrieved sequentially.

Using Select with Channels

The select statement allows a goroutine to wait on multiple channels, executing the case that is ready first.

package main

import (
    "fmt"
    "time"
)

func main() {
    ch1 := make(chan string)
    ch2 := make(chan string)
    
    go func() {
        time.Sleep(2 * time.Second)
        ch1 <- "Message from ch1"
    }()
    
    go func() {
        time.Sleep(1 * time.Second)
        ch2 <- "Message from ch2"
    }()
    
    select {
    case msg1 := <-ch1:
        fmt.Println(msg1)
    case msg2 := <-ch2:
        fmt.Println(msg2)
    }
}

Here, the select statement waits for the first available message and executes the corresponding case.

Conclusion

Concurrency in Go makes it easy to execute multiple tasks efficiently using goroutines and channels. By using these features, you can build responsive and high-performance applications.

In the next lesson, we will explore Error Handling in Go, a crucial aspect of writing robust programs.