DEV Community

Cover image for Concurrency in Go: Goroutines, Mutexes and Channels
Adrian DY
Adrian DY

Posted on

Concurrency in Go: Goroutines, Mutexes and Channels

Go (Golang) is a popular programming language that has been designed to support concurrency by making it easier for developers to write concurrent programs.

Goroutines are a key feature of the Go programming language (Golang) that make it easy to write concurrent code. Goroutines enable developers to execute multiple functions or methods at the same time, without blocking other parts of the program. In this article, we’ll explore how goroutines work, and how they can be used to write efficient and robust concurrent code.

What is a Goroutine?

In Golang, a goroutine is a lightweight thread of execution that is managed by the Go runtime. Goroutines enable developers to run multiple functions or methods concurrently, without the need for complex locking or synchronization mechanisms.

Creating a Goroutine

To create a new goroutine in Golang, use the go keyword, followed by the function or method that you want to execute concurrently. Here’s an example:

func main() {
    go myFunction()
    // do other work
}
func myFunction() {
    // do something
}
Enter fullscreen mode Exit fullscreen mode

In the example above, the myFunction() method is executed concurrently, in a new goroutine. The main program can continue to execute other code, without waiting for myFunction() to complete.

Synchronization and Sharing Data

Goroutines in Golang share the same memory space, which means they can share data between them. However, this can create synchronization issues, as multiple goroutines may attempt to modify the same data at the same time.

To prevent this, Golang provides synchronization primitives such as mutexes and channels. Mutexes are used to prevent multiple goroutines from accessing shared data simultaneously, while channels enable safe communication between multiple goroutines.

Mutexes

In Golang, mutexes are used to allow exclusive access to shared resources. A mutex is a mechanism that is used to prevent two goroutines from accessing a shared resource at the same time. When a goroutine acquires a mutex, all other goroutines attempting to acquire the same mutex will block until the mutex is released.

Here's an example of how to use a mutex in Golang:

import (
    "sync"
    "fmt"
)
 var count int
var mutex sync.Mutex
 func increment() {
    mutex.Lock()
    count++
    mutex.Unlock()
}
func main() {
    var wg sync.WaitGroup
     for i := 0; i < 1000; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            increment()
        }()
    }
    wg.Wait()
    fmt.Println(count)
}
Enter fullscreen mode Exit fullscreen mode

In this example, we're using a mutex to protect the count variable which is being used by multiple goroutines. The increment() function increments the count variable and uses the mutex.Lock() and mutex.Unlock() methods to ensure that the variable is being accessed by only one goroutine at a time. The end result is that the count variable is incremented by all the goroutines, resulting in a value of 1000.

Channels

Channels are a way for goroutines to communicate with each other. A channel allows one goroutine to send data to another goroutine. This is useful for sharing data between goroutines that might otherwise need to be protected with a mutex.

Here's an example of using a channel in Golang:

import (
    "fmt"
)
func producer(ch chan<- int) {
    for i := 0; i < 10; i++ {
        ch <- i
    }
    close(ch)
}
func consumer(ch <-chan int, done chan<- bool) {
    for value := range ch {
        fmt.Println("Received:", value)
    }
    done <- true
}
func main() {
    ch := make(chan int)
    done := make(chan bool)
    go producer(ch)
    go consumer(ch, done)
    <-done
}
Enter fullscreen mode Exit fullscreen mode

In this example, we've created a producer function that sends ten integers to a channel and then closes the channel. We've also created a consumer function that reads values from the channel and prints them. The main function creates the channel and a done channel to signal when the consumer has finished. When the consumer has finished reading from the channel, it sends a value to the done channel. The main function waits for the done channel to receive a value.

Conclusion

Goroutines are a powerful feature of the Go programming language that enable efficient and robust concurrent programming. By using goroutines, developers can write code that can execute multiple tasks simultaneously, without the need for complex locking or synchronization mechanisms. However, it’s important to be aware of synchronization issues when sharing data between goroutines, and to use synchronization primitives such as mutexes and channels to prevent them.

Mutexes and channels are powerful tools for managing concurrency in Golang programming. Mutexes can be used to ensure that shared resources are accessed in an exclusive manner, while channels enable goroutines to communicate with each other in a safe and efficient manner. By using mutexes and channels correctly, Golang developers can write efficient, scalable, and robust concurrent programs.

Top comments (0)