It’s not as if every block is sending out each line to run in a separate goroutine. Go encourages users to share data by communicating, not communicating by sharing data [0].
On phone, sorry for syntax
struct CMap {
sync.RWMutex
i int
}
func (m *CMap) Inc() {
m.RWLock()
defer m.Unlock()
m.i++
}
[0]: https://blog.golang.org/share-memory-by-communicatingfor instance, in a for loop ranging over a slice:
for _, i := range some_slice {
go func() {
something(i)
}()
}
i is re-used for every iteration of the loop, so the asynchronous function does not close over i as a particular value, but rather i as whatever value the loop has iterated to by the time it is referenced. i see experienced go programmers make this mistake sometimes, even when they've run into it before. my take is that the golang designers chose to implement this behaviour to either simplify their work or in the name of efficiency. if it's the former, it's indefensible. if it's the latter, why not offer user-friendly semantics (let closing over i as in the example capture the iterated value) and simplify the generated code at compile time when possible?for your example, you can look to the newly added sync/map which uses interface{} everywhere as an example of how the lack of generics have left users with no _sensible_ tools to remedy these kinds of language problems. how wonderful would an efficient thread-safe and type-safe concurrent map be? it's possible in many other modern and not so modern programming languages, but it is intentionally not possible in go.
for (int i : someCollection) {
pool.execute(() -> something(i));
}
Because "i" is effectively final, each Runnable closes over the correct value for that iteration. If "i" were being reassigned, the closure wouldn't be allowed to use it, you'd have to decide whether to close over a final copy or a reference to a mutable object that holds the latest value over time (a one-element array is the idiom).No, DO blame the language if its the one that enables and allows for said buggy code when it could as well prevent it.
Case in point: http://lambda-the-ultimate.org/node/3186
However a common situation is that the language is supporting a wide range of uses, not just the use a particular developer has in mind at the time.
I've seen this most commonly where a developer operating at a higher level of abstraction, without understanding low level details, complains that the language doesn't match his high level needs exactly.
When in fact it supports lower level operations for maximum performance or flexibility, while allowing layering on top for higher level uses. In language design it's essential to support the lowest level use cases you are targeting, because you can build on top but not go lower than the language exposes (unless it supports embedded asm or another trick to go lower again). I'm using higher/lower here only to refer in the sense of the level of abstraction.
It's a really common trap for someone working usually at one level of abstraction and not deeply understand other levels, to not understanding why a language or library is the way it is because if needs to support others users needs that are different to ones own - I've done it myself enough to detect the pattern, and seen lots of other people do it. Lurking on design committee discussions can be eye opening!
Don't blame the language for your buggy code.
Unless otherwise stated, Go's data structures are not thread-safe. Maps are not thread safe.
In that respect Go isn't different from any other mainstream language with pre-emptive threading (C++, Java, C#).