0

Below is an example of how to use mutex lock in order to safely access data. How would I go about doing the same with the use of CSP (communication sequential processes) instead of using mutex lock’s and unlock’s?

type Stack struct {
  top    *Element
  size   int
  sync.Mutex
}

func (ss *Stack) Len() int {
  ss.Lock()
  size := ss.size
  ss.Unlock()
  return size

}

func (ss *Stack) Push(value interface{}) {
  ss.Lock()
  ss.top = &Element{value, ss.top}
  ss.size++
  ss.Unlock()
}

func (ss *SafeStack) Pop() (value interface{}) {
  ss.Lock()
  size := ss.size
  ss.Unlock()
  if size > 0 {
    ss.Lock()
    value, ss.top = ss.top.value, ss.top.next
    ss.size--
    ss.Unlock()
    return
  }

  return nil
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Jan Bildus
  • 57
  • 6
  • Is this a homework question? Your code seems to be missing parts of it, including the definition of `Element`. – Duru Can Celasun Oct 19 '16 at 14:16
  • 2
    Why? A mutex is the correct primitive to use in this case? (also, it's much cleaner and less error prone to use `defer` https://play.golang.org/p/G-Y9sGooaQ) – JimB Oct 19 '16 at 14:16
  • 1
    The whole purpose of the channel paradigm is to get rid of mutexes. Using channels, you can synchronize without them. But to show you, we would need your actual code, not just the locking code. Usually to use channels effectively, one has to design the programs architecture around it. You cannot just change a few lines to use them effectively. – sinned Oct 19 '16 at 14:22
  • There's a race condition in your code — when you release the mutex to test if your stack is empty, another goroutine could pop the last element. – jch Nov 16 '16 at 01:11

1 Answers1

2

If you actually were to look at how Go implements channels, you'd essentially see a mutex around an array with some additional thread handling to block execution until the value is passed through. A channel's job is to move data from one spot in memory to another with ease. Therefore where you have locks and unlocks, you'd have things like this example:

func example() {
    resChan := make(int chan)
    go func(){
        resChan <- 1
    }()
    go func(){
        res := <-resChan
    }
}

So in the example, the first goroutine is blocked after sending the value until the second goroutine reads from the channel.

To do this in Go with mutexes, one would use sync.WaitGroup which will add one to the group on setting the value, then release it from the group and the second goroutine will lock and then unlock the value.

The oddities in your example are 1 no goroutines, so it's all happening in a single main goroutine and the locks are being used more traditionally (as in c thread like) so channels won't really accomplish anything. The example you have would be considered an anti-pattern, like the golang proverb says "Don't communicate by sharing memory, share memory by communicating."

Christian Grabowski
  • 2,782
  • 3
  • 32
  • 57