How can I elegantly do it in go
?
In python I could use attribute like this:
def function():
function.counter += 1
function.counter = 0
Does go
have the same opportunity?
How can I elegantly do it in go
?
In python I could use attribute like this:
def function():
function.counter += 1
function.counter = 0
Does go
have the same opportunity?
For example,
count.go
:
package main
import (
"fmt"
"sync"
"time"
)
type Count struct {
mx *sync.Mutex
count int64
}
func NewCount() *Count {
return &Count{mx: new(sync.Mutex), count: 0}
}
func (c *Count) Incr() {
c.mx.Lock()
c.count++
c.mx.Unlock()
}
func (c *Count) Count() int64 {
c.mx.Lock()
count := c.count
c.mx.Unlock()
return count
}
var fncCount = NewCount()
func fnc() {
fncCount.Incr()
}
func main() {
for i := 0; i < 42; i++ {
go fnc()
}
time.Sleep(time.Second)
fmt.Println(fncCount.Count())
}
Output:
$ go run count.go
42
Also, run the race detector,
$ go run -race count.go
42
See:
Introducing the Go Race Detector.
Benign data races: what could possibly go wrong?.
And here's a racy solution (@maerics answer is racy for the same reason),
package main
import (
"fmt"
"time"
)
var fncCount = 0
func fnc() {
fncCount++
}
func main() {
for i := 0; i < 42; i++ {
go fnc()
}
time.Sleep(time.Second)
fmt.Println(fncCount)
}
Output:
$ go run racer.go
39
And, with the race detector,
Output:
$ go run -race racer.go
==================
WARNING: DATA RACE
Read at 0x0000005b5380 by goroutine 7:
main.fnc()
/home/peter/gopath/src/so/racer.go:11 +0x3a
Previous write at 0x0000005b5380 by goroutine 6:
main.fnc()
/home/peter/gopath/src/so/racer.go:11 +0x56
Goroutine 7 (running) created at:
main.main()
/home/peter/gopath/src/so/racer.go:16 +0x4f
Goroutine 6 (finished) created at:
main.main()
/home/peter/gopath/src/so/racer.go:16 +0x4f
==================
42
Found 1 data race(s)
exit status 66
$
Let me quote the atomic package documentation:
Package atomic provides low-level atomic memory primitives useful for implementing synchronization algorithms. https://golang.org/pkg/sync/atomic/
Same code, but simpler and safe too.
package main
import (
"fmt"
"sync/atomic"
"time"
)
var fncCount uint64
func fnc() {
atomic.AddUint64(&fncCount, 1)
}
func main() {
for i := 0; i < 42; i++ {
go fnc()
}
// this is bad, because it won't wait for the goroutines finish
time.Sleep(time.Second)
fncCountFinal := atomic.LoadUint64(&fncCount)
fmt.Println(fncCountFinal)
}
$ go run -race main.go
42
var doThingCounter = 0
func DoThing() {
// Do the thing...
doThingCounter++
}