Learning GoLang - Day 6 : Channels and Mutexes in Goroutines
Unsurprisingly, writing concurrent programs looks much easier than other languages like C. You only need to understand three concepts:
Goroutine
Each goroutine is a lightweight thread of execution managed by Go runtime. So you don't need to worry about the real thread management, complex locking, or synchronization mechanisms, as a Golang developer. And Golang has the simplest way, at least as I know, of creating a thread(goroutine) by a single go
keyword.
func myFunc() {
// do something
}
func main() {
go myFunc()
}
The method following go
keyword is executed concurrently in a new goroutine.
However, there is a critical issue of synchronization as goroutines share the same memory space. Thus two synchronization primitives are commonly used to resolve this issue: Mutex and Channel.
Mutex
If you know C/C++, the concept of mutex is not something fresh to you. For the others, you only need to know mutex is equal to mutual exclusion. It is the key to avoid conflicts of accessing the same variable at the same time. Golang introduces sync.Mutex
, and its usage is no way simpler: two methods Lock
and Unlock
help us to keep the critical section safely accessible.
var count int
var mutex sync.Mutex
func increment() {
mutex.Lock()
count++
mutex.Unlock()
}
Channels
In contrast, channels help goroutines to communicate with each other safely when sharing data. For each channel, there is at least one sender and at least one receiver. The sender is responsible for sending the data to the entry of the channel and the receiver is waiting on the other side. Using channels is usually because of the use case of sharing data in order. You can easily send and receive values with the channel operator, <-
:
ch <- v // Send v to channel ch.
v := <-ch // Receive from ch, and
// assign value to v.
From my understanding, it is not a primitive technique, but more like an encapsulated approach for this common and popular use case. Apparently, you can achieve the same purpose by using mutex.
An example of using channels in Golang:
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
}
Conclusion
That is basically all you need to know as a Golang beginner. Goroutines are the basestone of Golang concurrent programming and mutexes and channels are necessary synchronization primitives to manage sharing data.
In the end, I list a few more tips worthy of investigating after you fully understand the above contents.
close
channels when necessary, e.g. inrange
- channels can be buffered
defer
+sync.Mutex.Unlock
select
+<-
lets a goroutine wait
The next day of learning Golang, I plan to focus on an existing open-source Go project. Learning from the real-world code, see you tomorrow!