Another incorrect idiom is busy waiting for a value, as in:
var a string var done bool func setup() { a = "hello, world" done = true } func main() { go setup() for !done { } print(a) }As before, there is no guarantee that, in
main, observing the write todoneimplies observing the write toa, so this program could print an empty string too. Worse, there is no guarantee that the write todonewill ever be observed bymain, since there are no synchronization events between the two threads. The loop inmainis not guaranteed to finish.
Thanks everyone. I agree using WaitGroup would result in much better readability.I also (think I) fully understand what rog is saying here:> If you're reading a variable in one goroutine that's being set by another> and you don't have any synchronisation between them, then your
> program behaviour is undefined.BUT just for the sake of this thought-experiment. IF said variable is a bool, when reading it, the program can only read true or false. Consider exactly this minimal step-by-step scenario:- main() sets the bool 'done' to false prior to any go-routine-call. main() is the only one ever reading 'done' and the only one ever setting it to false- then it calls the go-routine, which does stuff in parallel, then proceeds to do some of its own stuff, then waits (loops-blocks) until reading from 'done' yields true- this can only possibly happen after the go-routine did get the chance to set it to true prior to returning, which is the only 'done' access the go-routine performsNow, to be sure, I will most likely switch to a WaitGroup or a done channel anyway because of the very good arguments presented previously. But just to satisfy a budding Go Geek's technical curiosity... how can the above minimal setup ever become 'undefined'?
--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
You can presumably rely on any blocking event generatinga memory fence. One would expect that mutex unlocks would generate
a memory fence. (Is this documented somewhere?) It came up last June
that a close event on a channel was not a synchronizing operation,
although sending and receiving were. So using a channel close
as a Done flag was unsafe in older versions of Go.
See "http://comments.gmane.org/gmane.comp.lang.go.general/62107"
On 2/4/2013 12:12 PM, Philipp Schumann wrote:
> So, all this leads me to wonder: out of the various possible
> synchronization approaches -- channels, Mutex, RwMutex, WaitGroup -- do
> they *all* guarantee that immediately upon synchronization, *all* writes
> (performed in various variables / location) by the "job" goroutine will be
> observed by the "main" goroutine -- if not, which of those do guarantee
> this? If yes, which of them has the least amount of "overhead" /
> under-the-hood-work going on?
Now we start to get into issues such as "when do Go compilers
generate memory fences?" In the C/C++ world, there's endless
trouble around this, and the "volatile" qualifier to give some
control over it.
In the x86 world, the CPUs, for historical reasons, provide
reasonably strong guarantees of sequential memory access
across CPUs.
The current options for concurrency on mainstreammachines seem to be:
- No concurrency (BASIC)
- I/O concurrency only (Javascript)
- Safe message-passing concurrency (Erlang)
- Safe multiprogramming (Python, Go in single-thread mode)
- Safe concurrency via monitors (Ada)
- Unsafe concurrency via user-controlled locking
(C/C++, Java, Go in multi-thread mode)
Go makes unsafe concurrency easily accessible to more programmers.
In multi-thread mode, Go programmers suddenly have to be aware
of issues like out of order execution, effects of compiler
reordering, memory fences, and related low-level issues.
John Nagle