realcp1018
realcp1018

Reputation: 469

Use mutex or mutex pointer in Golang?

I have a code snippet:

type calculation struct{
    sum int
    mutex sync.Mutex
}

func dosomething(c *calculation , wg *sync.WaitGroup) {
    c.mutex.Lock()
    c.sum++
    c.mutex.Unlock()
    wg.Done()
}

func main() {
    t := time.Now()
    c := new(calculation)
    wg := new(sync.WaitGroup)
    for i:=0; i<10000000; i++{
        wg.Add(1)
        go dosomething(c, wg)
    }
    wg.Wait()
    fmt.Println(c.sum)
    fmt.Println(time.Since(t))
}

But I found that use a mutex pointer works too:

type calculation struct{
    sum int
    mutex *sync.Mutex
}

func dosomething(c *calculation , wg *sync.WaitGroup) {
    c.mutex.Lock()
    c.sum++
    c.mutex.Unlock()
    wg.Done()
}

func main() {
    t := time.Now()
    c := &calculation{0, new(sync.Mutex)}
    wg := new(sync.WaitGroup)
    for i:=0; i<10000000; i++{
        wg.Add(1)
        go dosomething(c, wg)
    }
    wg.Wait()
    fmt.Println(c.sum)
    fmt.Println(time.Since(t))
}

I made some tests on these 2 versions and found that there elapsed times are close.

So which one should I use? And Why should I use a mutex or mutex pointer? Are they have performance differences?

Upvotes: 0

Views: 2888

Answers (1)

torek
torek

Reputation: 489333

A Go sync.Mutex cannot be copied, so you would use a pointer to a (shared, non-copied) sync.Mutex when you have a data structure that can be copied sensibly but that has some portion of itself that is shared and cannot be copied sensibly, and you plan to pass it around by value and/or return it, so that it gets copied.

For instance:

type Thingy struct {
    notShared int
    mtx *Sync.mutex
    shared *int
}

func (t Thingy) Thingy {
    t.notShared += 3
    t.mtx.Lock()
    *t.shared *= 2
    t.mtx.Unlock()
    return t
}

(It would probably be more typical to have multiple non-shared fields, and then one pointer to a struct that contains both the mutex itself and the shared fields. The above is mainly for illustration)

Given that your calculation structure is itself always passed around as a pointer, though, there's no reason to add a level of indirection to your example.

Edit: you added a question about performance differences. Adding an extra (otherwise-unnecessary) indirection often costs a bit in performance, but in general, you should just write the most readable code you can first, and then measure it for performance issues. Adding an extra unnecessary indirection tends to make the code slightly less readable, in my view, so here, performance and readability go together.

Upvotes: 6

Related Questions