0

考虑以下代码:

type Cache struct{
    cache map[string]*http.Response
    AddChannel chan *http.Response
    RemoveChannel chan *http.Response
    FindChannel chan string
}

func (self *Cache) Run(){
    select{
        case resp := <-self.AddChannel:
        //..code
        case resp := <- self.RemoveChannel:
        //..code
        case find := <- self.FindChannel:
        //..code
    }
}

在这段代码中,创建了一个缓存,并在一个单独的 goroutine 上调用了 Run 函数。

如果要缓存响应,则通过缓存的AddChannel;发送。

如果要删除响应,则通过RemoveChannel

如果需要找到响应,则通过FindChannel.

这是一种保护缓存免受竞争条件影响的线程安全方式,还是有可能,例如,相同的响应可以发送到两者AddChannelRemoveChannel导致缓存损坏。

我已经阅读了 Go 的内存模型文档并了解可以保证在接收之前通过通道发送变量,但如果有多个通道与单个实例通信,我有点困惑这是否仍然成立。

对不起,如果我的问题措辞不好,感谢您的帮助。

4

2 回答 2

3

是的,选择只会等待或执行一个案例块。因此,如果您在任何时候只有一个 Run 函数,并且您知道没有其他 goroutine 会改变缓存,那么它将是无竞争的。

我假设您想要围绕选择进行无限循环。

这是一个示例,您可以在其中看到选择在执行时没有进入另一个块... https://play.golang.org/p/zFeRPK1h8c

顺便说一句,“self”作为接收者的名字不受欢迎。

于 2015-02-22T00:38:42.847 回答
3

原则上,使用通道是确保同步访问结构数据的有效方法。我在您的方法中看到的问题是您的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
}
于 2015-02-21T21:03:43.943 回答