Go concurrency plays an important role in go based application development.
Go supports Concurrency and not parallelism
If we have two tasks running on a multicore-processor, then each task is running independently or separately on each core and it is called parallelism. But for same two tasks if we have a single-core processor, then time-slice should be provided to each task to complete their execution and here it is called concurrency.
For Concurrency in Go we use Goroutines and channels. They are very similar to threads as used in other languages and also run concurrently with other functions or methods. Let’s see how a Goroutine looks like.
A Goroutine Example
import “fmt”
func myhello() {
fmt.Println(“My Hello World”)
}
func main() {
go myhello() //=====> Starting a goroutine
fmt.Println(“In main function”)
}
In main function
Here we have just seen how a goroutine looks like. But to make it work we have to modify it little bit. For now let’s understand some theory behind this.
Once you will run this program, you will get the output like:
In main function
Here, nothing will be print from myhello() Goroutine. The reason is when a new Goroutine starts, the main-Goroutine doesn’t wait for the new Goroutine to finish its execution. Hence here the myhello() Goroutine prints are ignored. Lets see how many ways we can fix this.
Lets fix this using time.sleep()
import (
“fmt”
“time”
)
func myhello() {
fmt.Println(“My Hello world”)
}
func main() {
go myhello()
time.Sleep(1 * time.Second)
fmt.Println(“In main function”)
}
My Hello world
In main function
Now you can see the outputs like:
My Hello world
In main function
Here both the statements are printed. The reason is we have used a sleep of 1 sec or i,e waiting for new goroutine myhello() to complete its execution, before coming out of the main go routine.
This is just to understand how a Goroutine works, but its not a proper way to implement a Goroutine.
Lets see how we can fix it using channels:–
golang channel example
Concurrency is achieved in Go using Goroutines. Channels help to synchronize them. Using channels, the Goroutines communicate among them.
import “fmt”
func myhello(ch chan bool) {
fmt.Println(“My Hello world goroutine”)
ch <- true //====> Writing to the channel ch
}
func main() {
ch1 := make(chan bool)
go myhello(ch1)
val := <-ch1 // ====> Reading from the channel ch1
fmt.Println(“in main function, Returned value is “, val)
}
My Hello world goroutine
in main function, Returned value is true
Here in main, we are waiting/reading from channel “ch1” and this waiting/reading will complete when someone will write something to this channel. So once the goroutine hello() complete its execution and write “true” to the channel then in the main it will able to read from the channel and executes further. Thus the output will be:–
My Hello world goroutine
in main function, returned value is true
Thus instead of using time.sleep(), we can achieve it using channels and this is one of the best way to resolve it.
WaitGroup:—
Using Waitgroup, we can wait for multiple goroutines to finish their work. Thus the control is blocked until all Goroutines finish there execution
package main
import (
“fmt”
“sync”
)
func dataCollector1(wg *sync.WaitGroup) {
fmt.Println(“In dataCollector1”)
wg.Done()
}
func dataCollector2(wg *sync.WaitGroup) {
fmt.Println(“In dataCollector2”)
wg.Done()
}
func main() {
wg := new(sync.WaitGroup)
wg.Add(2)
go dataCollector1(wg)
go dataCollector2(wg)
wg.Wait()
}
In dataCollector2
In dataCollector1
As above, we want the main gorouten should wait till both Goroutines are not complete. thus the output will be:–
Compared to go waitgroup, the go channel select works differently. It is used to choose from multiple send/receive channel operations and it blocks until one of the send/receive operation is ready
Channel deadlock: —
You will get a deadlock, when you are trying to read from a channel, but none is writing to that channel. Its sometimes called as go channel timeout
import “fmt”
func myhello(ch chan bool) {
fmt.Println(“Hello World”)
//ch <- true
}
func main() {
ch1 := make(chan bool)
go myhello(ch1)
fmt.Println(“Value read from channel is “, <-ch1)
}
Hello World
fatal error: all goroutines are asleep – deadlock!
goroutine 1 [chan receive]:
main.main()
/home/rjio/Go/src/MyGo/test.go:13 +0x79
exit status 2
As above you will get a deadlock, as you are trying to read from a channel “ch1”, where none is writing to it. i,e we have commented the line “ch <- true “, where we are trying to write into the same channel.