Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Within Golang struct shared among multiple goroutines, do non-shared members need mutex protection?

I have one Golang struct shared among multiple goroutines. For concurrent access to struct members, there is the mutex sync.RWMutex. For struct member that is accessed by one single goroutine, is there need of mutex protection?

For example, in the code below, one single writer goroutine accesses the member shared.exclusiveCounter, without any lock protection. Is this correct/safe? Or is there need of mutex because the whole struct is accessed by multiple goroutines thru a shared pointer?

package main

import (
    "fmt"
    "sync"
    "time"
)

func main() {
    s := &shared{mutex: &sync.RWMutex{}}

    readerDone := make(chan int)
    writerDone := make(chan int)

    go reader(s, readerDone)
    go writer(s, writerDone)

    <-readerDone
    <-writerDone
}

type shared struct {
    mutex            *sync.RWMutex
    sharedCounter    int // member shared between multiple goroutines, protected by mutex
    exclusiveCounter int // member exclusive of one goroutine -- is mutex needed?
}

func (s *shared) readCounter() int {
    defer s.mutex.RUnlock()
    s.mutex.RLock()
    return s.sharedCounter
}

func (s *shared) setCounter(i int) {
    defer s.mutex.Unlock()
    s.mutex.Lock()
    s.sharedCounter = i
}

func reader(s *shared, done chan<- int) {
    for {
        time.Sleep(2 * time.Second)
        counter := s.readCounter()
        fmt.Printf("reader: read counter=%d\n", counter)
        if counter > 5 {
            break
        }
    }
    fmt.Printf("reader: exiting\n")
    done <- 1
}

func writer(s *shared, done chan<- int) {
    s.exclusiveCounter = 0
    for {
        time.Sleep(1 * time.Second)
        s.exclusiveCounter++
        fmt.Printf("writer: writing counter=%d\n", s.exclusiveCounter)
        s.setCounter(s.exclusiveCounter)
        if s.exclusiveCounter > 5 {
            break
        }
    }
    fmt.Printf("writer: exiting\n")
    done <- 1
}

Run it on playground

like image 514
Everton Avatar asked Jan 29 '16 12:01

Everton


People also ask

Are a way to synchronize the access of shared resources between Goroutines?

Another way to synchronize access to a shared resource is by using a mutex . A mutex is named after the concept of mutual exclusion. A mutex is used to create a critical section around code that ensures only one goroutine at a time can execute that code section.

Are Goroutines concurrent?

Golang provides goroutines to support concurrency in Go. A goroutine is a function that executes simultaneously with other goroutines in a program and are lightweight threads managed by Go.

Can you have nested Goroutines?

Goroutines can be nested to an arbitrary depth and the output will be even more random in many cases.

How do Goroutines work internally?

A goroutine has a simple model: it is a function executing concurrently with other goroutines in the same address space. It is lightweight, costing little more than the allocation of stack space. And the stacks start small, so they are cheap, and grow by allocating (and freeing) heap storage as required.


2 Answers

If only a single goroutine accesses the struct member, you don't need to have a mutex to control access. I would, probably, use one anyway (either re-use the existing mutex in the struct, or another one), on the basis that while there may only be one goroutine accessing that struct member today, there's nothing enforce that.

like image 156
Vatine Avatar answered Oct 04 '22 13:10

Vatine


You do not need another mutex for it, also if you're just operating on int* types, you can ditch the mutex all together and use atomic.*

type shared struct {
    sharedCounter    int64 // member shared between multiple goroutines, protected by mutex
    exclusiveCounter int64 // member exclusive of one goroutine -- is mutex needed?
}

func (s *shared) readCounter() int64 {
    return atomic.LoadInt64(&s.sharedCounter)
}

func (s *shared) setCounter(i int64) {
    atomic.StoreInt64(&s.sharedCounter, i)
}

playground

like image 44
OneOfOne Avatar answered Oct 04 '22 11:10

OneOfOne