The variable in Goroutine not changed as expected - go

The codes are simple as below:
package main
import (
"fmt"
// "sync"
"time"
)
var count = uint64(0)
//var l sync.Mutex
func add() {
for {
// l.Lock()
// fmt.Println("Start ++")
count++
// l.Unlock()
}
}
func main() {
go add()
time.Sleep(1 * time.Second)
fmt.Println("Count =", count)
}
Cases:
Running the code without changing, u will get "Count = 0". Not expected??
Only uncomment line 16 "fmt.Println("Start ++")"; u will get output with lots of "Start ++" and some value with Count like "Count = 11111". Expected??
Only uncomment line 11 "var l sync.Mutex", line 15 "l.Lock()" and line 18 "l.Unlock()" and keep line 16 commented; u will get output like "Count = 111111111". Expected.
So... something wrong with my usage in shared variable...? My question:
Why case 1 had 0 with Count?
If case 1 is expected, why case 2 happened?
Env:
1. go version go1.8 linux/amd64
2. 3.10.0-123.el7.x86_64
3. CentOS Linux release 7.0.1406 (Core)

You have a data race on count. The results are undefined.
package main
import (
"fmt"
// "sync"
"time"
)
var count = uint64(0)
//var l sync.Mutex
func add() {
for {
// l.Lock()
// fmt.Println("Start ++")
count++
// l.Unlock()
}
}
func main() {
go add()
time.Sleep(1 * time.Second)
fmt.Println("Count =", count)
}
Output:
$ go run -race racer.go
==================
WARNING: DATA RACE
Read at 0x0000005995b8 by main goroutine:
runtime.convT2E64()
/home/peter/go/src/runtime/iface.go:255 +0x0
main.main()
/home/peter/gopath/src/so/racer.go:25 +0xb9
Previous write at 0x0000005995b8 by goroutine 6:
main.add()
/home/peter/gopath/src/so/racer.go:17 +0x5c
Goroutine 6 (running) created at:
main.main()
/home/peter/gopath/src/so/racer.go:23 +0x46
==================
Count = 42104672
Found 1 data race(s)
$
References:
Benign data races: what could possibly go wrong?

Without any synchronisation you have no guarantees at all.
There could be multiple reasons, why you see 'Count = 0' in your first case:
You have a multi processor or multi core system and one unit (cpu or core) is happily churning away at the for loop, while the other sleeps for one second and prints the line you are seeing afterwards. It would be completely legal for the compiler to generate machine code, which loads the value into some register and only ever increase that register in the for loop. The memory location can be updated, when the function is done with the variable. In case of an infinite for loop, that ist never. As you, the programmer told the compiler, that there is no contention about that variable, by omitting any synchronisation.
In your mutex version the synchronisation primitives tell the compiler,
that there might be some other thread taking the mutex, so it needs to write back the value from the register to the memory location before unlocking the mutex. At least one can think about it like that. What really happens that the unlock and a later lock operation introduce a happens before relation between the two go routines and this gives the guarantee, that we will see all writes to variables from before the unlock in one thread after the lock operation in the other thread, as described in go memory model locks howsoever this is implemented.
The Go runtime scheduler doesn't run the for loop at all, until the sleep in the main go routine is done. (Isn't likely, but, if I recall correctly, there is not guarantee that this isn't happening.) Sadly there is not much official documentation available about how the scheduler works in go, but it can only schedule a goroutine at certain points, it is not really preemptive. The consequences of this are severe. For example you could make your program run forever in some versions of go, by firing up as many go routines, as you had cores, doing endless for loops only incrementing a variable. There was no core left for the main go routine (which could end the program) and the scheduler can't preempt a go routine in an endless for loop doing only simple stuff, like incrementing a variable. I don't know, if that is changed now.
as others pointed out, that is a data race, google it and read up about it.
The difference between your versions there only line 16 is commented/uncommented is likely only because of run time, as printing to a terminal can be pretty slow.
For a correct program, you need to additionally lock the mutex after your sleep in you main program and before the fmt.Println and unlock it afterwards. But there can't be a deterministic expectation about the output, as the result will vary with machine/os/...

Related

Data race in Go: Why does it happen below 10-11ms?

Here is the code I ran:
package main
import (
"fmt"
"time"
)
const delay = 9 * time.Millisecond
func main() {
n := 0
go func() {
time.Sleep(delay)
n++
}()
fmt.Println(n)
}
Here is the command I used:
go run -race data_race_demo.go
Here is the behavior I noticed:
With delay set to 9ms or lower, data race is always detected (program throws Found 1 data race(s))
With delay set to 12ms or higher, data race is never detected (program simply prints 0)
With delay set to 10 to 11ms, data race occurs intermittently (that is, sometimes prints 0 and sometimes throws Found 1 data race(s))
Why does this happen at around 10-11ms?
I'm using Go 1.16.3 on darwin/amd64, if that matters.
You have 2 goroutines: the main and the one you launch. They access the n variable (and one is a write) without synchronization: that's a data race.
Whether this race is detected depends on whether this racy access occurs. When the main() function ends, your app ends as well, it does not wait for other non-main goroutines to finish.
If you increase the sleep delay, main() will end sooner than the end of sleep and will not wait for the n++ racy write to occur, so nothing is detected. If sleep is short, shorter than the fmt.Prinln() execution time, the racy write occurs and is detected.
There's nothing special about the 10ms. That's just the approximated time it takes to execute fmt.Println() and terminate your app in your environment. If you do other "lengthy" task before the Println() statement, such as this:
for i := 0; i < 5_000_000_000; i++ {
}
fmt.Println(n)
The race will be detected even with 50ms sleep too (because that loop will take some time to execute, allowing the racy write to occur before n is read for the fmt.Println() call and the app terminated). (A simple time.Sleep() would also do, I just didn't want anyone to draw the wrong conclusion that they "interact" with each other somehow.)

Is there a race condition in the golang implementation of mutex the m.state is read without atomic function

In golang if two goroutines read and write a variable without mutex and atomic, that may bring data race condition.
Use command go run --race xxx.go will detect the race point.
While the implementation of Mutex in src/sync/mutex.go use the following code
func (m *Mutex) Lock() {
// Fast path: grab unlocked mutex.
if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
if race.Enabled {
race.Acquire(unsafe.Pointer(m))
}
return
}
var waitStartTime int64
starving := false
awoke := false
iter := 0
old := m.state // This line confuse me !!!
......
The code old := m.state confuse me, because m.state is read and write by different goroutine.
The following function Test obvious has race condition problem. But if i put it in mutex.go, no race conditon will detect.
# mutex.go
func Test(){
a := int32(1)
go func(){
atomic.CompareAndSwapInt32(&a, 1, 4)
}()
_ = a
}
If put it in other package like src/os/exec.go, the conditon race problem will detect.
package main
import(
"sync"
"os"
)
func main(){
sync.Test() // race condition will not detect
os.Test() // race condition will detect
}
First of all the golang source always changes so let's make sure we are looking at the same thing. Take release 1.12 at
https://github.com/golang/go/blob/release-branch.go1.12/src/sync/mutex.go
as you said the Lock function begins
func (m *Mutex) Lock() {
// fast path where it will set the high order bit and return if not locked
if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
return
}
//reads value to decide on the lower order bits
for {
//if statements involving CompareAndSwaps on the lower order bits
}
}
What is this CompareAndSwap doing? it looks atomically in that int32 and if it is 0 it swaps it to mutexLocked (which is 1 defined as a const above) and returns true that it swapped it.
Then it promptly returns. That is its fast path. The goroutine acquired the lock and now it is running can start running it's protected path.
If it is 1 (mutexLocked) already, it doesn't swap it and returns false (it didn't swap it).
Then it reads the state and enters a loop that it does atomic compare and swaps to determine how it should behave.
What are the possible states? combinations of locked, woken and starving as you see from the const block.
Now depending on how long the goroutine has been waiting on the waitlist it will get priority on when to check again if the mutex is now free.
But also observe that only Unlock() can set the mutexLocked bit back to 0.
in the Lock() CAS loop the only bits that are set are the starving and woken ones.Yes you can have multiple readers but only one writer at any time, and that writer is the one who is holding the mutex and is executing its protected path until calling Unlock(). Check out this article for more details.
By disassemble the binary output file, The Test function in different pack generate different code.
The reason is that the compiler forbid to generate race detect instrument in the sync package.
The code is :
var norace_inst_pkgs = []string{"sync", "sync/atomic"} // https://github.com/golang/go/blob/release-branch.go1.12/src/cmd/compile/internal/gc/racewalk.go
``

Why race condition with goroutine won't happen some time?

I'm reading go-in-action. This example is from chapter6/listing09.go.
// This sample program demonstrates how to create race
// conditions in our programs. We don't want to do this.
package main
import (
"fmt"
"runtime"
"sync"
)
var (
// counter is a variable incremented by all goroutines.
counter int
// wg is used to wait for the program to finish.
wg sync.WaitGroup
)
// main is the entry point for all Go programs.
func main() {
// Add a count of two, one for each goroutine.
wg.Add(2)
// Create two goroutines.
go incCounter(1)
go incCounter(2)
// Wait for the goroutines to finish.
wg.Wait()
fmt.Println("Final Counter:", counter)
}
// incCounter increments the package level counter variable.
func incCounter(id int) {
// Schedule the call to Done to tell main we are done.
defer wg.Done()
for count := 0; count < 2; count++ {
// Capture the value of Counter.
value := counter
// Yield the thread and be placed back in queue.
runtime.Gosched()
// Increment our local value of Counter.
value++
// Store the value back into Counter.
counter = value
}
}
If you run this code in play.golang.org, it will be 2, same as the book.
But my mac print 4 most of the time, some time 2, some time even 3.
$ go run listing09.go
Final Counter: 2
$ go run listing09.go
Final Counter: 4
$ go run listing09.go
Final Counter: 4
$ go run listing09.go
Final Counter: 4
$ go run listing09.go
Final Counter: 4
$ go run listing09.go
Final Counter: 2
$ go run listing09.go
Final Counter: 4
$ go run listing09.go
Final Counter: 2
$ go run listing09.go
Final Counter: 3
sysinfo
go version go1.8.1 darwin/amd64
macOS sierra
Macbook Pro
Explanation from the book(p140)
Each goroutine overwrites the work of the other. This happens when the goroutine swap is taking place. Each goroutine makes its own copy of the counter variable and then is swapped out for the other goroutine. When the goroutine is given time to exe- cute again, the value of the counter variable has changed, but the goroutine doesn’t update its copy. Instead it continues to increment the copy it has and set the value back to the counter variable, replacing the work the other goroutine performed.
According to this explanation, this code should always print 2.
Why I got 4 and 3? Is it because race condition didn't happen?
Why go playground always get 2?
update:
After I set runtime.GOMAXPROCS(1), it starts to print 2, no 4, some 3.
I guess the play.golang.org is configured to have one logical processor.
The right result 4 without race condition. One logical processor means one thread. GO has same logical processors as the physical cores by default.So,
why one thread(one logical processor) leads to race condition while multiple thread print the right answer?
Can we say the explanation from the book is wrong since we also get 3 and 4 ?
How it get 3 ? 4 is correct.
Race conditions are, by definition, nondeterministic. This means that while you may get a particular answer most of the time, it will not always be so.
By running racy code on multiple cores you greatly increases the number of possibilities, hence you get a broader selection of results.
See this post or this Wikipedia article for more information on race conditions.

Main thread never yields to goroutine

edit * -- uncomment the two runtime lines and change Tick() to Sleep() and it works as expected, printing one number every second. Leaving code as is so answer/comments make sense.
go version go1.4.2 darwin/amd64
When I run the following, I never see anything printed from go Counter().
package main
import (
"fmt"
"time"
//"runtime"
)
var count int64 = 0
func main() {
//runtime.GOMAXPROCS(2)
fmt.Println("main")
go Counter()
fmt.Println("after Counter()")
for {
count++
}
}
func Counter() {
fmt.Println("In Counter()")
for {
fmt.Println(count)
time.Tick(time.Second)
}
}
> go run a.go
main
after Counter()
If I uncomment the runtime stuff, I will get some strange results like the following all printed at once (not a second apart):
> go run a.go
main
after Counter()
In Counter()
10062
36380
37351
38036
38643
39285
39859
40395
40904
43114
What I expect is that go Counter() will print whatever count is at every second while it is incremented continuously by the main thread. I'm not so much looking for different code to get the same thing done. My question is more about what is going on and where am I wrong in my expectations? The second results in particular don't make any sense, being printed all at once. They should never be printed closer than a second apart as far as I can tell, right?
There is nothing in your tight for loop that lets the Go scheduler run.
The schedule considers other goroutines whenever one blocks or on some (which? all?) function calls.
Since you do neither the easiest solution is to add a call to runtime.Gosched. E.g.:
for {
count++
if count % someNum == 0 {
runtime.Gosched()
}
}
Also note that writing and reading the same variable from different goroutines without locking or synchronization is a data race and there are no benign data races, your program could do anything when reading a value as it's being written. (And synchronization would also let the Go scheduler run.)
For a simple counter you can avoid locks by using atomic.AddInt64(&var, 1) and atomic.LoadInt64(&var).
Further note (as pointed out by #tomasz and completely missed by me), time.Tick doesn't delay anything, you may have wanted time.Sleep or for range time.Tick(…).

Golang: goroutine infinite-loop

When an fmt.Print() line is removed from the code below, code runs infinitely. Why?
package main
import "fmt"
import "time"
import "sync/atomic"
func main() {
var ops uint64 = 0
for i := 0; i < 50; i++ {
go func() {
for {
atomic.AddUint64(&ops, 1)
fmt.Print()
}
}()
}
time.Sleep(time.Second)
opsFinal := atomic.LoadUint64(&ops)
fmt.Println("ops:", opsFinal)
}
The Go By Example article includes:
// Allow other goroutines to proceed.
runtime.Gosched()
The fmt.Print() plays a similar role, and allows the main() to have a chance to proceed.
A export GOMAXPROCS=2 might help the program to finish even in the case of an infinite loop, as explained in "golang: goroute with select doesn't stop unless I added a fmt.Print()".
fmt.Print() explicitly passes control to some syscall stuff
Yes, go1.2+ has pre-emption in the scheduler
In prior releases, a goroutine that was looping forever could starve out other goroutines on the same thread, a serious problem when GOMAXPROCS provided only one user thread.
In Go 1.2, this is partially addressed: The scheduler is invoked occasionally upon entry to a function. This means that any loop that includes a (non-inlined) function call can be pre-empted, allowing other goroutines to run on the same thread.
Notice the emphasis (that I put): it is possible that in your example the for loop atomic.AddUint64(&ops, 1) is inlined. No pre-emption there.
Update 2017: Go 1.10 will get rid of GOMAXPROCS.

Resources