I'm interested in using a Go package that uses a global variable which, for my application, is a key "variable". Each time I call this package, I want to set that variable (it's exported). And I'm calling it from various Go routines. Is there a recommended strategy for sequencing/syncing my calls to that package so that my code isn't colliding on that global variable?
Here's an example:
package main
import (
"fmt"
"sync"
"time"
"github.com/jameshaskell/_sketches/globalvars/testlib"
)
var wg sync.WaitGroup
func gr(i int) {
defer wg.Done()
testlib.GlobalVar = i
duration := time.Duration(i) * time.Millisecond
time.Sleep(duration)
fmt.Printf(" pause %d DONE
", i)
fmt.Printf(" global var: %d should be: %d TestFunc: %s
", testlib.GlobalVar, i, testlib.TestFunc())
}
func main() {
for i := 0; i <= 10; i += 1 {
wg.Add(1)
go gr(i)
}
wg.Wait()
}
The package I'm trying to access would be like:
package testlib
import "fmt"
var GlobalVar int
func TestFunc() string {
return fmt.Sprintf("GlobalVar: %d
", GlobalVar)
}
NOTE: the package I'm hoping to use (having the global var) isn't mine...
Is this variable intended to be used this way? May be there’re some specific setters/getters for it? Does documentation have some info about?
I think code becomes very fragile because you do not control all accesses to the variable. You may serialize access from your code with a mutex. But another lib’s code doesn’t know about your it. Moreover it can change suddenly in new versions or bug fixes.
So I prefer:
To read about it in official docs
If documentation allows, and I can’t avoid to use it, protect access to the variable with sync/mutex/atomic/channel depending on a task.
Prepare tests to cover different cases with the variable
Run race condition detector as part of my CI tests.
Use the atomic package with the Value
type
You could use the atomic package, but beware the warning there:
These functions require great care to be used correctly. Except for special, low-level applications, synchronization is better done with channels or the facilities of the sync package. Share memory by communicating; don't communicate by sharing memory.
Or you could use sync.Mutex to protect the variable but this would require wrapping it in a struct or an access function to properly control access.
There is a broader question though which you should attempt to answer - if you set this variable every time you access the package, why is it a package variable at all? Why not instead just pass it in to the function which needs it? Since it changes every time anyway, there's little point in a package variable quite apart from the synchronisation problems it poses.