Skip to main content

Threading and Concurrency with Go

Table of Contents

Concurrency in Go
#

Go provides built-in support for concurrent programming, making it easy to write programs that can handle multiple tasks simultaneously. Concurrency in Go is based on two main concepts: goroutines and channels.

Goroutines
#

A goroutine is a lightweight thread of execution. You can start a new goroutine by using the go keyword before a function call. The function will then run concurrently with the calling function.

package main

import (
    "fmt"
    "time"
)

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

func main() {
    go say("world")
    say("hello")
}

In this example, go say("world") starts a new goroutine. The main function continues to execute, and both functions run concurrently.

Channels
#

Channels are a typed conduit through which you can send and receive values with the <- operator. Channels can be used to communicate between goroutines.

package main

import "fmt"

func sum(s []int, c chan int) {
    sum := 0
    for _, v := range s {
        sum += v
    }
    c <- sum // send sum to 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)
}

This example creates two goroutines to calculate the sum of two halves of a slice. The results are sent back to the main function through a channel.

Select
#

The select statement lets a goroutine wait on multiple communication operations. A select blocks until one of its cases can run, then it executes that case. It chooses one at random if multiple are ready.

package main

import (
    "fmt"
    "time"
)

func main() {
    c1 := make(chan string)
    c2 := make(chan string)

    go func() {
        time.Sleep(1 * time.Second)
        c1 <- "one"
    }()
    go func() {
        time.Sleep(2 * time.Second)
        c2 <- "two"
    }()

    for i := 0; i < 2; i++ {
        select {
        case msg1 := <-c1:
            fmt.Println("received", msg1)
        case msg2 := <-c2:
            fmt.Println("received", msg2)
        }
    }
}

Sync Package
#

The sync package provides basic synchronization primitives such as mutual exclusion locks. These are useful for protecting shared data from being accessed by multiple goroutines at the same time.

  • sync.Mutex: A mutual exclusion lock.
  • sync.RWMutex: A reader/writer mutual exclusion lock.
  • sync.WaitGroup: Waits for a collection of goroutines to finish.
  • sync.Once: An object that will perform exactly one action.
  • sync.Cond: A condition variable, a rendezvous point for goroutines waiting for or announcing the occurrence of an event.

Related