I. Introduction

One of the very well known techniques to handle concurrency in golang is using the mutex. To those whom don’t really know about inter-process communication. We have two methods for it:

  • Shared memory: very efficient but lead to the result that threads could step on each other ⇒ we handle that by using Mutex, WaitGroup…
  • Message passing: produce messages consumed by the consumer. the message is produced and consumed once!

We will discuss about Shared memory and Message passing, particularly is its advantages and drawbacks in another posts. Now, let’s focus on Mutex.

1. Why do we need mutex?

  • For example: When we using goroutines, there will be highly chance that threads step over the other one, this may make the gobal variable that handled by that thread is not correct as expected. Now we need to use mutex to lock the peice of code that we don’t want the other thread do something with it. And after that, we unlock it.

2. When can use the mutex?

  • To protect global variable that accessed by many threads (goroutines)
  • We can use the mutex when we need to manage the cache or state

3. Mutex or the other technique?

  • Since channel is awesome, don’t hesitate to use mutex. In fact, I do see many public project using mutex.

II. Implementation

1. The Problem

Now imagine your program is handling an ecommerce business. You want your stock number of a product to remain the same every time (sounds crazy ^^). So, the warehouse is for adding the product to the stock (increasing). And at the same time, the sales department also selling it, meaning the number of stock getting decrease. Since we have two threads running together, the code looks like this:

package main

// thread could step on each other, this is the example for it,
// run it without lock Mutex then you will see the money at the result may will be diffirent from 100

import (
	"time"
)

var (
	stocks = 100
)

func takeProduct() {
	for i := 0; i <= 100000; i++ { // add the product 100000 time
		stocks += 1
		// time.Sleep(1 * time.Millisecond)
	}
	println("Sell 1")
}

func addProduct() {
	for i := 0; i <= 100000; i++ {
		stocks -= 1
		// time.Sleep(1 * time.Millisecond)
	}
	println("Adding 1")
}

func main() {
	go takeProduct()
	go addProduct()
	time.Sleep(2000 * time.Millisecond)
	println(stocks)
}

Let the process run in 20 seconds, We are expecting that the stock number will stay 100 after the process is done.

But this is the result, completely wrong!

The problem is, those two threads are stepping on each other, so instead of alway getting minus 1 and add 1. Sometimes the one thread can’t perform its action (That is also the problem of the shared memory technique).

2. The Solution

The syntax of basic mutex is something like this:

var lock  = sync.Mutex{}
lock.Lock()
lock.Unlock()

So, by locking the peice of code that perfor the addition or substraction. We ensure that:

At the ame time, only one thread is touching the global variable, the other don’t, the have to wait.

OK, we have this:

package main

// thread could step on each other, this is the example for it,
// run it without lock Mutex then you will see the money at the result may will be diffirent from 100

import (
	"sync"
	"time"
)

var (
	stocks = 100
	lock   = sync.Mutex{}
)

func takeProduct() {
	for i := 0; i <= 100000; i++ {
		lock.Lock() // lock, then the other thread can't touch to the `stocks` variable
		stocks += 1
		lock.Unlock() // Now we unlock it so the other can't worl with `stocks`
	}
	println("Sell 1")
}

func addProduct() {
	for i := 0; i <= 100000; i++ {
		lock.Lock()
		stocks -= 1
		lock.Unlock()
	}
	println("Adding 1")
}

func main() {
	go takeProduct()
	go addProduct()
	time.Sleep(10000 * time.Millisecond)
	print(stocks)
}

Let see the result:

Yep, it is supposed to be like that!

III. Conclusion

  • Everytime you seeing that you need to work with some thing need the consistency, that involved in mayny threads but has to keep the corection, please considering use mutex!
  • We will have another post digging deeper about mutex

One thought on “Mutex in Golang – Simple and useful!

Leave a Reply

Your email address will not be published. Required fields are marked *