-2

I'm learning concurrency-related issues in Golang. I wrote some code:

package main

import (
    "fmt"
    "time"
)

func incr(num *int) {
    *num = *num + 1

}

func main() {
    var a = 0

    for i := 0; i < 50; i++ {
        go incr(&a)
    }

    incr(&a)

    time.Sleep(1 * time.Second)
    fmt.Println(a)
}

The result of this code is: 51

In this code I've declared a variable which I'm increasing in 50 running goroutines. What I've read and unsterstood this code should fail because multiple goroutines are writing to same memory address. In this case I should add sync.Mutex lock in order to fix that.

Code is available in the playground: https://play.golang.org/p/Tba9pfpxaHY

Could You explain what is really happening in this program?

PiKey
  • 628
  • 5
  • 24
  • 3
    It doesn't work. You just got "lucky". Perhaps you're running on a single-CPU machine (such as the Go playground)? Run it with the race detector (`go run -race`). – Jonathan Hall May 23 '19 at 11:22
  • IMO, This is why concurrent programming is next level difficult :) No explicit errors or panics, may work under observation. While it's *extremely* likely that the race detector detects this specific race condition there are conditions/situations where the race detector will NOT detect anything. This is where IMO we need to still fall back on first principles and learn to recognize all concurrent accesses (https://medium.com/dm03514-tech-blog/golang-candidates-and-contexts-a-heuristic-approach-to-race-condition-detection-e2b230e70d08) and guard by default – dm03514 May 23 '19 at 12:55

1 Answers1

5

Guess what? I ran your app and I get varying outputs: sometimes 49, sometimes 48, sometimes 50 (and sometimes 51).

If you run your app with the race detector enabled (go run -race play.go), it tells you have data races:

==================
WARNING: DATA RACE
Read at 0x00c00009a010 by goroutine 7:
  main.incr()
      /home/icza/gows/src/play/play.go:9 +0x3a

Previous write at 0x00c00009a010 by goroutine 6:
  main.incr()
      /home/icza/gows/src/play/play.go:9 +0x50

Goroutine 7 (running) created at:
  main.main()
      /home/icza/gows/src/play/play.go:17 +0x83

Goroutine 6 (finished) created at:
  main.main()
      /home/icza/gows/src/play/play.go:17 +0x83
==================

When you have data races, the behavior of your app is undefined. "Seemingly working sometimes" also fits into the "undefined" behavior, but undefined also means it can do anything else too.

See related questions:

Assign a map to another map is safety in golang?

Is it safe to read a function pointer concurrently without a lock?

golang struct concurrent read and write without Lock is also running ok?

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
icza
  • 389,944
  • 63
  • 907
  • 827
  • I'd also point at [this classic piece](https://software.intel.com/en-us/blogs/2013/01/06/benign-data-races-what-could-possibly-go-wrong) by one of the folks behind that race detector ;-) – kostix May 23 '19 at 13:59
  • @kostix Yes, that link is also included in one of my linked answers, but it can't hurt to advertise it more. – icza May 23 '19 at 14:01