How to resolve the algorithm Mutex step by step in the Go programming language
How to resolve the algorithm Mutex step by step in the Go programming language
Table of Contents
Problem Statement
A mutex (abbreviated Mutually Exclusive access) is a synchronization object, a variant of semaphore with k=1. A mutex is said to be seized by a task decreasing k. It is released when the task restores k. Mutexes are typically used to protect a shared resource from concurrent access. A task seizes (or acquires) the mutex, then accesses the resource, and after that releases the mutex. A mutex is a low-level synchronization primitive exposed to deadlocking. A deadlock can occur with just two tasks and two mutexes (if each task attempts to acquire both mutexes, but in the opposite order). Entering the deadlock is usually aggravated by a race condition state, which leads to sporadic hangups, which are very difficult to track down.
Let's start with the solution:
Step by Step solution about How to resolve the algorithm Mutex step by step in the Go programming language
First Approach:
This code demonstrates synchronization using a mutex and a wait group. The slowInc
function increments a shared variable value
after sleeping for a period of time, while being protected by a mutex.
value
is a global variable of typeint
that is shared between the goroutines.m
is async.Mutex
that is used to protect access tovalue
.wg
is async.WaitGroup
that is used to wait for all goroutines to complete.
The main function creates two goroutines that call the slowInc
function. The wait group is used to ensure that both goroutines have finished before printing the value of value
.
Explanation:
- The
slowInc
function locks the mutex (m.Lock()
) before accessing the shared variablevalue
. This ensures that only one goroutine can accessvalue
at a time. - The function then sleeps for a period of time (
time.Sleep(1e8)
) to simulate a slow operation. - After sleeping, the function increments
value
and unlocks the mutex (m.Unlock()
). - The function signals that it is done by calling
wg.Done()
. - The main function waits for both goroutines to complete (
wg.Wait()
). - Finally, the main function prints the value of
value
.
Second Approach:
This code demonstrates synchronization using channels. The slowInc
function increments a shared variable value
after sleeping for a period of time, while being protected by a channel.
value
is a global variable of typeint
that is shared between the goroutines.ch
is a channel of typebool
that is used as a mutex.done
is a channel of typebool
that is used to signal completion.
The main function creates two goroutines that call the slowInc
function. Each goroutine receives from the channel ch
before incrementing value
. After incrementing value
, each goroutine sends a value to the channel ch
and signals completion by sending a value to the channel done
. The main function waits for both goroutines to complete before printing the value of value
.
Explanation:
- The
slowInc
function receives from the channelch
(<-ch
), which represents acquiring the mutex. This blocks the goroutine until the channel has a value, which happens when the other goroutine releases the mutex. - The function then sleeps for a period of time (
time.Sleep(1e8)
) to simulate a slow operation. - After sleeping, the function increments
value
and sends a value to the channelch
(ch <- true
), which represents releasing the mutex. - The function then signals completion by sending a value to the channel
done
(done <- true
). - The main function waits for both goroutines to complete by receiving from the channel
done
twice (<-done
). - Finally, the main function prints the value of
value
.
In both approaches, the shared variable value
is protected from concurrent access by synchronization primitives. The difference is that the first approach uses a mutex and a wait group, while the second approach uses channels.
Source code in the go programming language
package main
import (
"fmt"
"sync"
"time"
)
var value int
var m sync.Mutex
var wg sync.WaitGroup
func slowInc() {
m.Lock()
v := value
time.Sleep(1e8)
value = v+1
m.Unlock()
wg.Done()
}
func main() {
wg.Add(2)
go slowInc()
go slowInc()
wg.Wait()
fmt.Println(value)
}
package main
import (
"fmt"
"time"
)
var value int
func slowInc(ch, done chan bool) {
// channel receive, used here to implement mutex lock.
// it will block until a value is available on the channel
<-ch
// same as above
v := value
time.Sleep(1e8)
value = v + 1
// channel send, equivalent to mutex unlock.
// makes a value available on channel
ch <- true
// channels can be used to signal completion too
done <- true
}
func main() {
ch := make(chan bool, 1) // ch used as a mutex
done := make(chan bool) // another channel used to signal completion
go slowInc(ch, done)
go slowInc(ch, done)
// a freshly created sync.Mutex starts out unlocked, but a freshly created
// channel is empty, which for us represents "locked." sending a value on
// the channel puts the value up for grabs, thus representing "unlocked."
ch <- true
<-done
<-done
fmt.Println(value)
}
You may also check:How to resolve the algorithm Compile-time calculation step by step in the Pascal programming language
You may also check:How to resolve the algorithm Check Machin-like formulas step by step in the Raku programming language
You may also check:How to resolve the algorithm Dinesman's multiple-dwelling problem step by step in the C++ programming language
You may also check:How to resolve the algorithm A+B step by step in the Wee Basic programming language
You may also check:How to resolve the algorithm Ackermann function step by step in the FreeBASIC programming language