3

我试图了解 Golang典型的数据竞争之一,其中从多个 goroutine 访问未受保护的全局变量可能会导致竞争条件:

var service map[string]net.Addr

func RegisterService(name string, addr net.Addr) {
  service[name] = addr
}

func LookupService(name string) net.Addr {
  return service[name]
}

它接着说,我们可以通过使用互斥锁保护它来解决这个问题:

var (
  service   map[string]net.Addr
  serviceMu sync.Mutex
)

func RegisterService(name string, addr net.Addr) {
  serviceMu.Lock()
  defer serviceMu.Unlock()
  service[name] = addr
}

func LookupService(name string) net.Addr {
  serviceMu.Lock()
  defer serviceMu.Unlock()
  return service[name]
}

到目前为止,一切都很好。让我困惑的是:

这个问题的公认答案表明,受 CPU 限制的 goroutine 将使任何其他已被多路复用到同一 OS 线程上的 goroutine 饿死(除非我们明确地使用 yield runtime.Gosched())。这是有道理的。

对我来说,上面的RegisterService()andLookupService()函数看起来受 CPU 限制,因为没有 IO 也没有产量。这个对吗?

如果是,并且如果GOMAXPROCS 设置为 1,那么上面示例中的互斥锁仍然是绝对必要的吗?goroutine 在可能发生竞争条件的地方受 CPU 限制这一事实是否会解决这个问题?

即使是这样,我认为在现实生活中使用互斥锁仍然是一个好主意,因为我们可能无法保证 GOMAXPROCS 的设置。还有其他原因吗?

4

1 回答 1

0

正如 FUZxxl 和 Nick Craig-Wood 所指出的,goroutines 的当前行为是特定于实现的。所以,也许,读或写地图可以产生。考虑到映射不是线程安全的,因此正确的并发访问需要互斥锁或其他同步。

作为互斥锁的替代方案,您可以生成一个 goroutine,它在您的地图上执行所有操作并通过通道与其对话:

type writereq struct {
    key string
    value net.Addr
    reply chan struct{}
}

type readreq struct {
    key string
    reply chan net.Addr
}

var service map[string]net.Addr
var reads = make(chan readreq)
var writes = make(chan writereq)

func RegisterService(name string, addr net.Addr) {
    w := writereq{name, addr, make(chan struct{})}
    writes <- w
    return <-w.reply // return after registration confirmation
}

func LookupService(name string) net.Addr {
    r := readreq{name, make(chan net.Addr)}
    reads <- r
    return <-r.reply
}

func serveRegistry() {
    for {
        select {
        case r := <-reads:
            r.reply <- service[r.name]
        case w := <-writes:
            service[w.name] = w.addr
            w.reply <- struct{}
        }
    }
}
于 2013-10-30T23:32:08.097 回答