Sling Academy
Home/Golang/Concurrency and Synchronization in Go: Understanding the Basics

Concurrency and Synchronization in Go: Understanding the Basics

Last updated: November 27, 2024

Go, also known as Golang, is a statically typed, compiled, and concurrent programming language designed at Google. One of its key features is built-in support for concurrency. In this article, we will explore the basics of concurrency and synchronization in Go.

Goroutines

In Go, concurrency is achieved through goroutines, which are lightweight threads managed by the Go runtime. They are extremely easy to work with compared to traditional threads.

package main

import (
    "fmt"
    "time"
)

func say(s string) {
    for i := 0; i < 3; i++ {
        time.Sleep(100 * time.Millisecond)
        fmt.Println(s)
    }
}

func main() {
    go say("world") // running in a goroutine
    say("hello")      // running in the main thread
}

In the example above, the function say is called as a goroutine by using the go keyword. While say("world") runs concurrently, the main function continues to execute and calls say("hello") concurrently.

Channels

Channels are the conduits through which goroutines communicate. You can send a value into a channel from one goroutine and receive that value in another goroutine.

package main

import (
    "fmt"
)

func sum(s []int, c chan int) {
    sum := 0
    for _, v := range s {
        sum += v
    }
    c <- sum // send sum to channel c
}

func main() {
    s := []int{7, 2, 8, -9, 4, 0}

    c := make(chan int)
    go sum(s[:len(s)/2], c)
    go sum(s[len(s)/2:], c)
    x, y := <-c, <-c // receive from c

    fmt.Println(x, y, x+y)
}

In this example, the sum function calculates the sum of integers in a slice and sends it to a channel c. The main function runs two goroutines that compute the sum concurrently and then receives their results from the channel.

Buffered Channels

Channels can be buffered, allowing sending and receiving to proceed independently up to a set maximum. These are declared by passing an additional capacity argument to the channel instantiation.

package main

import "fmt"

func main() {
    c := make(chan int, 2) // buffered channel with a capacity of 2

    c <- 1
    c <- 2

    fmt.Println(<-c)
    fmt.Println(<-c)
}

In a buffered channel, the sends do not block until the buffer is full. In the code example, the channel c can hold two values. The main goroutine sends value 1 and 2 to the channel before reading them.

Synchronization and WaitGroups

Go's sync package provides synchronization primitives, which include the WaitGroup to wait for a collection of goroutines to finish executing.

package main

import (
    "fmt"
    "sync"
)

func worker(id int, wg *sync.WaitGroup) {
    defer wg.Done()
    fmt.Printf("Worker %d starting\n", id)

    // simulate work
    time.Sleep(time.Second)

    fmt.Printf("Worker %d done\n", id)
}

func main() {
    var wg sync.WaitGroup

    for i := 1; i <= 3; i++ {
        wg.Add(1)
        go worker(i, &wg)
    }

    wg.Wait() // wait for all workers to finish
}

In this example, the main function launches three worker goroutines and waits until they have completed using a WaitGroup.

Conclusion

Concurrency in Go makes it straightforward to run multiple tasks independently, leveraging goroutines and channels for efficient execution and communication. Synchronization can be effectively managed using channels and the sync package.

Next Article: Goroutines Explained: Lightweight Concurrency in Go

Series: Concurrency and Synchronization in Go

Golang

Related Articles

You May Also Like

  • How to remove HTML tags in a string in Go
  • How to remove special characters in a string in Go
  • How to remove consecutive whitespace in a string in Go
  • How to count words and characters in a string in Go
  • Relative imports in Go: Tutorial & Examples
  • How to run Python code with Go
  • How to generate slug from title in Go
  • How to create an XML sitemap in Go
  • How to redirect in Go (301, 302, etc)
  • Using Go with MongoDB: CRUD example
  • Auto deploy Go apps with CI/ CD and GitHub Actions
  • Fixing Go error: method redeclared with different receiver type
  • Fixing Go error: copy argument must have slice type
  • Fixing Go error: attempted to use nil slice
  • Fixing Go error: assignment to constant variable
  • Fixing Go error: cannot compare X (type Y) with Z (type W)
  • Fixing Go error: method has pointer receiver, not called with pointer
  • Fixing Go error: assignment mismatch: X variables but Y values
  • Fixing Go error: array index must be non-negative integer constant