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

huangapple go评论78阅读模式
英文:

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

问题

我有一个在多个goroutine之间共享的Golang结构体。
为了并发访问结构体成员,可以使用互斥锁sync.RWMutex。
对于只被单个goroutine访问的结构体成员,是否需要互斥锁保护?

例如,在下面的代码中,只有一个写入goroutine访问成员shared.exclusiveCounter,没有任何锁保护。这样做是否正确/安全?或者因为整个结构体通过共享指针被多个goroutine访问,所以需要互斥锁?

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 // 多个goroutine之间共享的成员,由互斥锁保护
	exclusiveCounter int // 单个goroutine独占的成员--是否需要互斥锁?
}

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
}

在这个例子中,由于shared.exclusiveCounter只被单个goroutine访问,且没有其他goroutine访问该成员,所以不需要互斥锁保护。但是,由于整个结构体通过共享指针被多个goroutine访问,所以需要对shared.sharedCounter进行互斥锁保护。

英文:

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 (
&quot;fmt&quot;
&quot;sync&quot;
&quot;time&quot;
)
func main() {
s := &amp;shared{mutex: &amp;sync.RWMutex{}}
readerDone := make(chan int)
writerDone := make(chan int)
go reader(s, readerDone)
go writer(s, writerDone)
&lt;-readerDone
&lt;-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&lt;- int) {
for {
time.Sleep(2 * time.Second)
counter := s.readCounter()
fmt.Printf(&quot;reader: read counter=%d\n&quot;, counter)
if counter &gt; 5 {
break
}
}
fmt.Printf(&quot;reader: exiting\n&quot;)
done &lt;- 1
}
func writer(s *shared, done chan&lt;- int) {
s.exclusiveCounter = 0
for {
time.Sleep(1 * time.Second)
s.exclusiveCounter++
fmt.Printf(&quot;writer: writing counter=%d\n&quot;, s.exclusiveCounter)
s.setCounter(s.exclusiveCounter)
if s.exclusiveCounter &gt; 5 {
break
}
}
fmt.Printf(&quot;writer: exiting\n&quot;)
done &lt;- 1
}

[Run it on playground][1]

[1]: http://play.golang.org/p/gzOsuYOAKm "Run it on playground"

答案1

得分: 4

如果只有一个goroutine访问结构体成员,你不需要使用互斥锁来控制访问。我可能会使用一个(可以重用结构体中的现有互斥锁,或者另一个互斥锁),因为虽然今天可能只有一个goroutine访问该结构体成员,但没有强制要求只能有一个。

英文:

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.

答案2

得分: 2

你不需要为此再添加另一个互斥锁,而且如果你只是在操作int*类型的数据,你可以完全放弃使用互斥锁,而是使用atomic.*函数来操作。

type shared struct {
    sharedCounter    int64 // 在多个goroutine之间共享的成员,由互斥锁保护
    exclusiveCounter int64 // 一个goroutine独有的成员 -- 是否需要互斥锁?
}

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

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

playground

英文:

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(&amp;s.sharedCounter)
}
func (s *shared) setCounter(i int64) {
atomic.StoreInt64(&amp;s.sharedCounter, i)
}

<kbd>playground</kbd>

huangapple
  • 本文由 发表于 2016年1月29日 20:10:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/35084268.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定