Mutex

Mutual exclusion locks are a fundamental synchronization primitive used in many programming languages to protect shared resources from concurrent access.

Go also offers sync.Mutex. It can be easily implemented using a buffered channel.

There is also RWMutex which allows multiple readers to access the shared resource concurrently, and ensures that the writer has exclusive access by blocking other readers and writers.

a naive counter

package main

import (
	"fmt"
	"sync"
)

type Conter struct {
	value int64
}

func (c *Conter) Inc() {
	c.value++
}

func (c *Conter) Value() int64 {
	return c.value
}

func main() {
	counter := &Conter{}
	var wg sync.WaitGroup

	inc := func() {
		counter.Inc()
		wg.Done()
	}

	for i := 0; i < 100; i++ {
		wg.Add(3)
		go inc()
		go inc()
		go inc()
	}
	wg.Wait()
	fmt.Println(counter.Value())
}
$ go run counter.go                                                                                                                                                                                                                                                                                                                         <<<
267

using Mutex

package main

import (
	"fmt"
	"sync"
)

type Conter struct {
	mu    sync.Mutex
	value int64
}

func (c *Conter) Inc() {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.value++
}

func (c *Conter) Value() int64 {
	return c.value
}

func main() {
	counter := &Conter{}
	var wg sync.WaitGroup

	inc := func() {
		counter.Inc()
		wg.Done()
	}

	for i := 0; i < 100; i++ {
		wg.Add(3)
		go inc()
		go inc()
		go inc()
	}
	wg.Wait()
	fmt.Println(counter.Value())
}
$ go run mutex.go                                                                                                                                                                                                                                                                                                                         <<<
300

implementing Mutex using channel

package main

import (
	"fmt"
	"sync"
)

type Mutex chan struct{}

func NewMutex() Mutex {
	return make(Mutex, 1)
}

func (m Mutex) Lock() {
	m <- struct{}{}
}

func (m Mutex) Unlock() {
	<-m
}

type Counter struct {
	mu    Mutex
	value int64
}

func NewCounter() *Counter {
	return &Counter{mu: NewMutex()}
}

func (c *Counter) Inc() {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.value++
}

func (c *Counter) Value() int64 {
	return c.value
}

func main() {
	counter := NewCounter()

	var wg sync.WaitGroup
	inc := func() {
		counter.Inc()
		wg.Done()
	}

	for i := 0; i < 100; i++ {
		wg.Add(3)
		go inc()
		go inc()
		go inc()
	}
	wg.Wait()
	fmt.Println(counter.Value())
}