How to resolve the algorithm Mutex step by step in the Go programming language

Published on 12 May 2024 09:40 PM
#Go

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 type int that is shared between the goroutines.
  • m is a sync.Mutex that is used to protect access to value.
  • wg is a sync.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:

  1. The slowInc function locks the mutex (m.Lock()) before accessing the shared variable value. This ensures that only one goroutine can access value at a time.
  2. The function then sleeps for a period of time (time.Sleep(1e8)) to simulate a slow operation.
  3. After sleeping, the function increments value and unlocks the mutex (m.Unlock()).
  4. The function signals that it is done by calling wg.Done().
  5. The main function waits for both goroutines to complete (wg.Wait()).
  6. 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 type int that is shared between the goroutines.
  • ch is a channel of type bool that is used as a mutex.
  • done is a channel of type bool 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:

  1. The slowInc function receives from the channel ch (<-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.
  2. The function then sleeps for a period of time (time.Sleep(1e8)) to simulate a slow operation.
  3. After sleeping, the function increments value and sends a value to the channel ch (ch <- true), which represents releasing the mutex.
  4. The function then signals completion by sending a value to the channel done (done <- true).
  5. The main function waits for both goroutines to complete by receiving from the channel done twice (<-done).
  6. 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