在多个goroutine之间共享的Golang结构中,非共享成员是否需要互斥保护?

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

我有一个在多个 goroutine 之间共享的 Golang 结构。 对于结构成员的并发访问,有互斥量 sync.RWMutex。 对于单个goroutine访问的struct member,是否需要互斥保护?

例如,在下面的代码中,一个单独的 writer goroutine 访问成员 shared.exclusiveCounter,没有任何锁保护。这是correct/safe吗?或者是否需要互斥量因为整个结构由多个 goroutines 通过共享指针访问?

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

如果只有一个goroutine访问struct成员,你不需要有一个mutex来控制访问。无论如何,我可能会使用一个(重新使用结构中现有的互斥量,或者另一个),基于虽然今天可能只有一个 goroutine 访问该结构成员,但没有任何强制执行。

你不需要另一个互斥锁,如果你只是在 int* 类型上操作,你可以一起抛弃互斥锁并使用 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