This question already has answers here:
How safe are Golang maps for concurrent Read/Write operations?
(8 answers)
Closed 4 months ago.
Firstly, I have already read this How safe are Golang maps for concurrent Read/Write operations? and my question is not how safe golang maps are during read/write but a lot more specific. In multiple places in the above mentioned thread at SO, people have mentioned that Concurrent read from maps are still ok! and logically that should be the case but it turns out that there are some other resources as well where people mention golang maps are not even safe to read concurrently. For example take a look at the following resources
https://go.dev/blog/maps
https://www.youtube.com/watch?v=BywIJqYodl4
In one place https://groups.google.com/g/golang-nuts/c/_XHqFejikBg I saw people mentioned that reads are concurrent if it happens only after the initialisation of Maps. How sync.Map is playing here? Why reads are not concurrent if each goroutines process for a single key but writes happens after the map is initialised via other goroutines? It shouldn't be the case because for each key hash is being calculated and for a given hash write/read never happens by any other goroutines.
Please help to explain it. Thanks
If you create an initialize a map, and then create goroutines and read from it concurrently, it is safe, there is no need to serialize access. The initialization of the map must be completed before the goroutines start reading from it. There is a nuance here though: the map initialization must be complete before concurrent reads. If you complete the initialization before you create the goroutines, then it is safe. If you create the goroutines before map initialization is complete, then you have to make sure goroutines wait until initialization is complete by using a synchronization mechanism like a channel.
Related
This question already has an answer here:
How to make a variable thread-safe
(1 answer)
Closed 8 months ago.
I have a lot of functions in every routines I'm planning to run, but before I reconstruct it to multiple threads, it uses a global variable to send message back.
And after I reconstruct it, the routines write the same variable which causes panic.
So, how could I solve this problem? Is there a way to make a unique global variable for every routine?
Don't use global (package level) variables. If you must, use proper synchronization.
But instead as Go's proverb goes:
Do not communicate by sharing memory; instead, share memory by communicating.
Use channels instead to communicate results which is safe for concurrent use by design.
If there is state all goroutines should have, group the variables describing the state into a struct, and pass a value of that struct to each goroutine, or have them create their own.
See related: How to make a variable thread-safe
I could not find anything about this question except this explanation by Wikipedia https://en.wikipedia.org/wiki/Channel_(programming). But I'm not satisfied with the explanation.
What problem do channels solve?
Why don't we just use normal variables to send and receive data instead?
If by "normal variables" you mean, for example, a slice that multiple goroutines write to and read from, then this is a guaranteed way to get data races (you don't want to get data races). You can avoid concurrent access by using some kind of synchronization (such as Mutex or RWLock).
At this point, you
reinvented channels (which are basically that, a slice under a mutex)
spent more time than you needed to and still your solution is inferior (there's no syntax support, you can't use your slices in select, etc.)
Channels solve the problem of concurrent read and write. Basically, prevent the situation when one goroutine reads a variable and another one writes the same variable.
Also channels may have buffer, so you can write several values before locking.
Of course, you don't have to use channels. There are other ways to send data between goroutines. For example, you can use atomic operations when assigning or reading a value from a shared variable, or use mutex whenever you access it.
I came across this reddit comment saying that one should not pass pointers to channel or a struct with pointers fields but it didn't give enough explaination
It's perfectly fine to send pointers on a channel if you're being careful about it and understand that what comes out on the other end is still a pointer to the same data and not a copy.
That is, if you're using the channel to communicate between two goroutines, you ought to be careful about accessing the pointer from two different goroutines at the same time - because that could be a data race (if one of the goroutines is modifying the underlying data). However, if this is taken into account then there is no safety issue.
As tile, I am referring to Go package sync.Map, can its functions be considered as atomic? Mainly the Load, Store, LoadOrStore, and Delete function.
I also build a simple example go playground, is it guaranteed that only one goroutine can enter the code range line 15 - 17? As my test seems it can be guaranteed.
Please help to explain.
The godoc page for the sync package says: "Map is like a Go map[interface{}]interface{} but is safe for concurrent use by multiple goroutines without additional locking or coordination."
This statement guarantees that there's no need for additional mutexes or synchronization across goroutines. I wouldn't call that claim "atomic" (which has a very precise meaning), but it does mean that you don't have to worry about multiple goroutines being able to enter a LoadOrStore block (with the same key) like in your example.
I am writing a golang program that has to perform multiple download requests at a time utilizing goroutines running in parallel (using GOMAXPROCS). In addition, there is a form of state kept, which is which components have been downloaded and which components are left to be downloaded. The mutex solution would be to lock this structure keeping track of which components have been successfully downloaded. I have read that when attempting to keep state, mutexes are the best option.
However, I am wondering what would be a solution utilizing channels (passing ownership instead of providing exclusive access to state) instead of mutexes, or are mutexes the best option?
P.S.
So far I have thought of passing the global structure keeping state between go routines which are all utilizing one channel (a read-write channel). A go routine attempts to read the structure from the channel and then write it back when it's done.The problem I found with this, is that when the last running go routine [assume all others have finished and stopped running] gives up its posession of the structure by writing to the channel, it will result in deadlock since there are no receivers. In addition, this is still attempting to use channels as mutexes [attempting to provide exclusive access].