I’ve been thinking for a while about “porting” The Little Book of Semaphores to Go. What I mean by that is taking the examples and problems presented in the book and implementing them in idiomatic Go. With that goal in mind, I started a blog where I expect to present the material as I develop it. While I don’t rule it out, for the moment my goal is not to (partially) rewrite the book (although the license does allow for that). My immediate goal is to provide a “transition guide”, as some of the patterns presented in the book are generally applicable.
For the past few weeks I’ve been looking around at some of the confusion that newbies to the language have, and there’s quite a bit of it in the are of mutexes and semaphores. Having a good go-specific resource would be lovely.
Excellent little article. Very clear.
I am preparing an example about ‘cyclic barrier’. I will use this latter semaphore implementation with sync.Cond. Just done a quick benchmark, 20.4ns on Decrease()/Increment() (Acquire/Release) with it and 21.6ns with the robryk implementation (just for the record).
I have added 2 ‘cyclic barrier’ (could be applied to Matrix computation - rows as ‘parties’) examples, one using ‘sync.Cond’ and another using a ‘Mutex/Chan’.
Hope these 2 examples help exploring Go signalling with the ‘sync’ package primitives.
@emicklei: Regarding, the question: “when to use sync.Cond ?”. Well, I would say after all options in sync have been exhausted (WaitGroup is quite useful) and when a ‘condition/event’ state is to be modified atomically (happens safely) and is to trigger a ‘release’ on other waiting ‘threads/goroutines’ (ie signalling when a condition is safely satisfied), but I do not know whether saying this, this way, answer anything (I will try to figure out a more pertinent example). “Cyclic barrier” tries to show a situation when sync.Cond is really an efficient signalling scheme.
Complement: Channels are inherently hazard safe, and blocking and signaling are done automatically, simpler to use than Sync.Cond which is a lower level primitive requiring to manage ‘safely’ acquiring and releasing locks and signalling. To be used when a fine-grained control is necessary (when dealing with multiple consumer threads, to control pool of threads).
See condition variable article in Akhter, S., & Roberts, J. (2006). Multi-Core Programming: Increasing Performance through Software Multi-threading. Intel Press. According to them “The condititon variables are preferable to locks when pooling requires and needs some scheduling behavior among threads”.
The standard library uses ‘sync.Cond’ in ‘crypto/buffer.go’, signalling one at a time (no broadcast) in a producer(Writer signal())/consumer(Reader(s) wait() until buffer contains something…) scheme. See here: https://github.com/golang/crypto/blob/master/ssh/buffer.go.
I am preparing an example about the Dining Philosophers problem. And just read an interesting paper “the Driving Philosophers”, an attempt to generalize the Dining Philosophers, see here: http://infoscience.epfl.ch/record/64443/files/tcs2004.pdf.
I published my Go solution to the Dining Philosophers a couple of weeks ago. I’d love to see what you came up with, Patrick.
I played with some variations of the solution (is a fork a channel? a goroutine? a piece of data?) and I settled down on the one presented in the blog post. I tried to not stray away too much from the original problem, but after thinking about it, I feel the original problem is designed with a semaphore solution in mind.