Go Concurrency
Go supports concurrency, and we can start a goroutine simply by using the go
keyword.
A goroutine is a lightweight thread managed by the Go runtime.
The syntax for a goroutine is:
go function_name( argument_list )
For example:
go f(x, y, z)
To start a new goroutine:
f(x, y, z)
Go allows the use of the go
statement to start a new runtime thread, a goroutine, to execute a function with a newly created goroutine. All goroutines in the same program share the same address space.
Example
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")
}
When you run the above code, you will see that the outputs of "hello" and "world" are not in a fixed order. This is because they are being executed by two separate goroutines:
world
hello
hello
world
world
hello
hello
world
world
hello
Channels
A channel is a data structure used to transfer data.
Channels can be used between two goroutines to synchronize their execution and communicate by passing a value of a specified type. The operator <-
is used to specify the direction of the channel, send or receive. If no direction is specified, it is a bidirectional channel.
ch <- v // Send v to channel ch
v := <-ch // Receive from ch, and
// assign value to v
Declaring a channel is straightforward; we use the chan
keyword, and the channel must be created before use:
ch := make(chan int)
Note: By default, channels are unbuffered. The sender blocks until the receiver receives the data.
The following example calculates the sum of numbers using two goroutines. After the goroutines complete their calculations, it sums the two results:
Example
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 channel c
fmt.Println(x, y, x+y)
}
The output will be:
-5 17 12
Channel Buffer
Channels can be buffered, specified by the second argument to make
:
ch := make(chan int, 100)
Buffered channels allow sending data and receiving data to be asynchronous. The sender can place data into the buffer and wait for the receiver to retrieve it, rather than requiring immediate retrieval.
However, since the buffer size is limited, the receiver must still retrieve data; otherwise, once the buffer is full, the sender cannot send more data.
Note: If the channel is unbuffered, the sender blocks until the receiver receives the value. If the channel is buffered, the sender blocks until the value is copied into the buffer. If the buffer is full, it waits until some receiver retrieves a value. The receiver blocks until there is a value to receive.
Example
package main
import "fmt"
func main() {
// Here we create a buffered channel that can store integers
// with a buffer size of 2
ch := make(chan int, 2)
// Since ch is buffered, we can send two data without needing immediate synchronization
ch <- 1
ch <- 2
// Retrieve these two data
fmt.Println(<-ch)
fmt.Println(<-ch)
}
The output will be:
1
2
fmt.Println(<-ch)
fmt.Println(<-ch)
}
Execution output:
1
2
Traversing Channels and Closing Channels in Go
Go uses the range
keyword to iterate over received data, similar to arrays or slices. The format is as follows:
v, ok := <-ch
If the channel cannot receive data, ok
becomes false
, and the channel can be closed using the close()
function.
Example
package main
import (
"fmt"
)
func fibonacci(n int, c chan int) {
x, y := 0, 1
for i := 0; i < n; i++ {
c <- x
x, y = y, x+y
}
close(c)
}
func main() {
c := make(chan int, 10)
go fibonacci(cap(c), c)
// The range function iterates over each data received from the channel. Since c sends 10
// data and then closes the channel, the range function ends after receiving 10 data.
// If the c channel is not closed, the range function will not end, and it will block
// when trying to receive the 11th data.
for i := range c {
fmt.Println(i)
}
}
Execution output:
0
1
1
2
3
5
8
13
21
34