计算函数被调用的次数[关闭]

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?

var doThingCounter = 0
func DoThing() {
  // Do the thing...
  doThingCounter++
}

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?.

The Go Memory Model


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