Golang Concurrency — Keep it simple and stupid
Sep 2, 2018 · 2 min read
The biggest selling point of GoLang is its Go Rountines for concurrency.
Following are important points regarding Go Routines:
- Concurrency is a primitive in golang just as datatypes like int, float etc.
- Among all languages that provide concurrency, I find GoLang has the most simplistic approach.
- Go rountines speed up a program to a great extent in multi-core processors.
- Golang go routines are independent of the actual number of OS threads. Hence, without worrying about thrashing (when the overhead of context switching in threads exceed actual execution) one can spawn any number of go routines. Typically in Java, one has to use a thread pool to keep a fixed number of threads and submit tasks to threads.
- It would also be incorrect to term Go-Routines as green threads since they don’t run/managed in any VM.
Go Routines
The following program finds the number of prime numbers in a given range using go routines:
package main
import "fmt"
func isPrime(num int) bool {
for i:=2; i< num; i++ {
if num%i == 0 {
return false
}
}
return true
}
func getPrimesCountAsync(start, end int) chan int {
resChan := make(chan int)
go func() {
cnt := 0
for i:= start; i<=end; i++ {
if isPrime(i) {
cnt++
}
}
resChan <- cnt
close(resChan)
}()
return resChan
}
func main() {
inputs := [][]int { {4, 58000}, {20, 95000}, {50, 148000}, {1020, 5400}, {4, 48000}, {4, 90000} }
resChans := make([]chan int, len(inputs))
for idx, input := range inputs {
resChans[idx] = getPrimesCountAsync(input[0], input[1])
}
for _, resChan := range resChans {
fmt.Println(<- resChan)
}
}As we can see, constructs are very simple.
- go func() spawns a new co-routine.
- chan can be used for communication with main thread.
Timing:
time go run playground.go25.26s user 0.31s system 209% cpu 12.213 total
Comparison with single threaded program
Following is a simple single threaded program:
package main
import "fmt"
func isPrime(num int) bool {
for i:=2; i< num; i++ {
if num%i == 0 {
return false
}
}
return true
}
func getPrimesCountSync(start, end int) int {
cnt := 0
for i:= start; i<=end; i++ {
if isPrime(i) {
cnt++
}
}
return cnt
}
func main() {
inputs := [][]int { {4, 58000}, {20, 95000}, {50, 148000}, {1020, 5400}, {4, 48000}, {4, 90000} }
for _, input := range inputs {
fmt.Println(getPrimesCountSync(input[0], input[1]))
}
}Timing
time go run playground.go
18.33s user 0.27s system 99% cpu 18.750 totalConclusion
- Speed up: We can see while single threaded program takes 18.750s, multi-threaded go routine program takes 12.213s.
- Simplicity: The multi-threaded program is very much similar to the single-threaded program except it returns a channel of result instead of the result itself and doesn’t require importing any additional utilities.
Thanks to Nirmal Ganesh M and Rajdeep Das for reviewing the article.
