原则上,使用通道是确保同步访问结构数据的有效方法。我在您的方法中看到的问题是您的Run
函数只执行一次读取然后返回。只要您Run
每次都从同一个 goroutine 调用,它可能会起作用,但有一种更简单的方法。
内存安全只能保证所有结构访问仅限于一个,并且只有一个,goroutine。我通常这样做的方式是创建一个在通道上循环的轮询例程。要么无限期地,要么直到它被明确停止。
这是一个例子。我为每个支持的操作创建单独的通道,主要是为了更清楚正在发生的事情。您可以轻松地使用单个通道,如chan interface{}
,并打开接收到的消息类型以查看您应该执行哪种操作。这种设置非常松散地基于 Erlang 的消息传递概念。它需要相当多的样板来设置,但不需要互斥锁。它是否有效和可扩展是您只能通过测试发现的东西。另请注意,它包含相当数量的分配开销。
package main
import "fmt"
func main() {
t := NewT()
defer t.Close()
t.Set("foo", 123)
fmt.Println(t.Get("foo"))
t.Set("foo", 321)
fmt.Println(t.Get("foo"))
t.Set("bar", 456)
fmt.Println(t.Get("bar"))
}
type T struct {
get chan getRequest
set chan setRequest
quit chan struct{}
data map[string]int
}
func NewT() *T {
t := &T{
data: make(map[string]int),
get: make(chan getRequest),
set: make(chan setRequest),
quit: make(chan struct{}, 1),
}
// Fire up the poll routine.
go t.poll()
return t
}
func (t *T) Get(key string) int {
ret := make(chan int, 1)
t.get <- getRequest{
Key: key,
Value: ret,
}
return <-ret
}
func (t *T) Set(key string, value int) {
t.set <- setRequest{
Key: key,
Value: value,
}
}
func (t *T) Close() { t.quit <- struct{}{} }
// poll loops indefinitely and reads from T's channels to do
// whatever is necessary. Keeping it all in this single routine,
// ensures all struct modifications are preformed atomically.
func (t *T) poll() {
for {
select {
case <-t.quit:
return
case req := <-t.get:
req.Value <- t.data[req.Key]
case req := <-t.set:
t.data[req.Key] = req.Value
}
}
}
type getRequest struct {
Key string
Value chan int
}
type setRequest struct {
Key string
Value int
}