Reputation: 21
As stated in this link https://golang.org/ref/mem, the code below uses an incorrect synchronization:
var a, b int
func f() {
a = 1
b = 2
}
func g() {
print(b)
print(a)
}
func main() {
go f()
g()
}
Because it's possible that it prints a = 0 and b = 2.
However, I wonder if this result is possible in the code below where a and b are protected with Lock:
var a, b int
var mu sync.Mutex
func f() {
mu.Lock()
a = 1
b = 2
mu.Unlock()
}
func g() {
mu.Lock()
print(b)
print(a)
mu.Unlock()
}
func main() {
go f()
g()
}
Because the link says:
For any sync.Mutex or sync.RWMutex variable l and n < m, call n of l.Unlock() happens before call m of l.Lock() returns.
However, it is not clear about whether the assignment of a and b is guaranteed to be executed before the Unlock
statement
Upvotes: 0
Views: 1258
Reputation: 418337
The code is safe in regard that it has no data race.
The behavior is not defined though. There is no synchronization regarding goroutine scheduling, so it may be that g()
locks the mutex first, and once it releases, main()
ends and your app may end with it, and f()
might not complete (might not even start). The app may print nothing, may print 2
, or may print 21
.
It may also be that f()
locks first, carriers out the assignments, and then g()
in main()
will print the assigned new values: 21
.
If you want f()
to assign first, you may use a sync.WaitGroup
, e.g.:
var a, b int
var wg sync.WaitGroup
func f() {
defer wg.Done()
a = 1
b = 2
}
func g() {
print(b)
print(a)
}
func main() {
wg.Add(1)
go f()
wg.Wait()
g()
}
This will always print 21
, try it on the Go Playground.
And a convoluted example to prove it can be solved with sync.Mutex
too. This is not its designed use, but this also works:
Lock mu
in the main()
, before launching the goroutine. Have g()
also lock, which will obviously block at first. f()
can unlock it once its job is done, giving "green light" to g()
:
var a, b int
var mu sync.Mutex
func f() {
a = 1
b = 2
mu.Unlock()
}
func g() {
mu.Lock()
print(b)
print(a)
mu.Unlock()
}
func main() {
mu.Lock()
go f()
g()
}
Try this one on the Go Playground.
Upvotes: 4
Reputation: 51602
It is guaranteed that once f
executes mu.Unlock()
, all goroutines reading a
and b
will see the updated values of a
and b
provided those goroutines also access a
and b
using the same lock. If there are goroutines that read a
and b
without lock then there is a race.
Upvotes: 3