DEV Community

Cover image for Concurrency in Go/Golang: A Comprehensive Guide
Prathamesh Tamanekar
Prathamesh Tamanekar

Posted on

Concurrency in Go/Golang: A Comprehensive Guide

Concurrency is a concept which if understood and implemented correctly can improve the performance of your system and code exponentially. It allows you to make the most out of the processing resources available at your disposal.

Concurrency is the ability to run multiple tasks simultaneously. It is a key feature of Go, which makes it well-suited for building high-performance and scalable applications.

Go's concurrency model is based on two key concepts:

  • Goroutines: Goroutines are lightweight threads of execution. They are extremely efficient, and can be created and switched between with very little overhead.
  • Channels: Channels are a way for goroutines to communicate with each other. They can be used to send and receive data between goroutines, and to synchronize their execution.

Go's slogan:

Do not communicate by sharing memory; instead share memory by communicating.

This slogan encapsulates Go's approach to concurrency. Goroutines should not share memory directly, instead they should communicate with each other using channels. This helps to avoid race conditions and other concurrency bugs.

Goroutines

To create a goroutine, simply add the go keyword before calling a function. For example:

go func() {
  // Do something concurrently
}()
Enter fullscreen mode Exit fullscreen mode

Goroutines are multiplexed onto a small number of OS threads. This means that they can run concurrently, but they are not necessarily running on separate CPUs.

Channels

Channels are a way for goroutines to communicate with each other. They can be used to send and receive data between goroutines, and to synchronize their execution.

To create a channel, use the make() function. The make() function takes two arguments: the type of data that the channel will carry, and the capacity of the channel. The capacity of a channel is the maximum number of values that can be buffered in the channel at any time.

For example, to create a channel that can carry integer values and has a capacity of 10, you would use the following code:

ch := make(chan int, 10)
Enter fullscreen mode Exit fullscreen mode

To send a value to a channel, use the <- operator. For example, to send the value 10 to the channel ch, you would use the following code:

ch <- 10
Enter fullscreen mode Exit fullscreen mode

To receive a value from a channel, also use the <- operator. For example, to receive a value from the channel ch and store it in the variable x, you would use the following code:

x := <-ch
Enter fullscreen mode Exit fullscreen mode

If there are no values in the channel, the <- operator will block until a value is sent to the channel.

Buffered channels

Buffered channels can store a certain number of values before blocking. This can be useful for applications where goroutines need to be able to send and receive data without having to block each other.

To create a buffered channel, simply specify the capacity of the channel when you create it. For example, to create a buffered channel that can carry integer values and has a capacity of 10, you would use the following code:

ch := make(chan int, 10)
Enter fullscreen mode Exit fullscreen mode

Once the channel is full, any goroutines that try to send values to the channel will be blocked until space becomes available. Similarly, any goroutines that try to receive values from an empty channel will be blocked until a value is sent to the channel.

Range and close

A sender can close a channel to indicate that there are no more values to be sent. Sending close is only necessary when the receiver must be told there are no more values coming. Such as to terminate a range loop.

For example, the following code shows how to use a range loop to receive all of the values from a channel:

for x := range ch {
  // Do something with the value `x`
}
Enter fullscreen mode Exit fullscreen mode

When the sender closes the channel, the range loop will terminate.

Select

The select statement can be used to wait on multiple channels simultaneously. This can be useful for applications where you need to respond to different events in a timely manner.

For example, the following code shows how to use a select statement to wait on two channels:

select {
case x := <-ch1:
  // Do something with the value `x`
case y := <-ch2:
  // Do something with the value `y`
}
Enter fullscreen mode Exit fullscreen mode

If there is a value available on either channel, the select statement will return immediately. If there are no values available on either channel, the select statement will block until a value becomes available.

Summary

Concurrency is a powerful feature of Go that can be used to build high-performance and scalable applications. By understanding how to use goroutines and channels, you can write code that is both efficient and easy to maintain.

Top comments (0)