Go: Mutual exclusion lock (mutex)

Package: sync

Mutexes let you synchronize data access by explicit locking, without channels.

lock

Example: Here's a basic example illustrating Lock and Unlock:
mutex := sync.Mutex{}
mutex.Lock()
go func() {
	mutex.Lock() // Wait for main to call Unlock
	fmt.Println("In goroutine")
	mutex.Unlock()
}()
time.Sleep(time.Second)
fmt.Println("In main")
mutex.Unlock()
time.Sleep(time.Second)
"In main" will be printed before "In goroutine", despite the fact that the main routine sleeps for a second before the call to Println.

Use with caution!

For this type of locking to be safe, it’s crucial that all accesses to the shared data, both reads and writes, are performed only when a goroutine holds the lock. One mistake by a single goroutine is enough to introduce a data race and break the program.

Because of this you should consider designing a custom data structure with a clean API and make sure that all the synchronization is done internally. Here’s a more interesting example:

Example: In this example we build a safe and easy-to-use concurrent data structure, AtomicInt, that stores a single integer. Any number of goroutines can safely access this number through the Add and Value methods.
// AtomicInt is a concurrent data structure that holds an int.
// Its zero value is 0.
type AtomicInt struct {
    mu sync.Mutex // A lock than can be held by one goroutine at a time.
    n  int
}

// Add adds n to the AtomicInt as a single atomic operation.
func (a *AtomicInt) Add(n int) {
    a.mu.Lock() // Wait for the lock to be free and then take it.
    a.n += n
    a.mu.Unlock() // Release the lock.
}

// Value returns the value of a.
func (a *AtomicInt) Value() int {
    a.mu.Lock()
    n := a.n
    a.mu.Unlock()
    return n
}

func main() {
    wait := make(chan struct{})
    var n AtomicInt
    go func() {
        n.Add(1) // one access
        close(wait)
    }()
    n.Add(1) // another concurrent access
    <-wait
    fmt.Println(n.Value()) // Output: 2
}

Comments