dsdqpdjpq16640651 2013-09-28 11:34
浏览 141

GOMAXPROCS = 1时的数据争用

I'm trying to understand one of the Golang typical data races where access to an unprotected global variable from multiple goroutines may cause a race condition:

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]
}

It goes on to say that we can solve this by protecting it with a mutex:

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]
}

So far, so good. What's confusing me is this:

The accepted answer to this question suggests that a CPU-bound goroutine will any starve other goroutines that have been multiplexed onto the same OS thread (unless we explicitly yield with runtime.Gosched()). Which makes sense.

To me, the RegisterService() and LookupService() functions above look to be CPU bound, as there's no IO and no yield. Is this correct?

If it is, and if GOMAXPROCS is set to 1, then is a mutex in the above example still strictly necessary? Doesn't the fact that the goroutines are CPU-bound at the point where race conditions might occur take care of it?

Even if it does, I presume that in real life using a mutex is here is still a good idea as we may not be able to guarantee what GOMAXPROCS is set to. Are there another reasons?

  • 写回答

1条回答 默认 最新

  • doumengjing1500 2013-10-30 23:32
    关注

    As FUZxxl and Nick Craig-Wood noted, current behavior of goroutines is implementation-specific. So, maybe, read or write to map can yield. Considering that maps are not thread safe, mutex or another syncronization is required for correct concurrent access.

    As alternative to mutex, you can spawn a goroutine, that performs all operations on your map and talk to it with channels:

    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{}
            }
        }
    }
    
    评论

报告相同问题?

悬赏问题

  • ¥15 公交车和无人机协同运输
  • ¥15 stm32代码移植没反应
  • ¥15 matlab基于pde算法图像修复,为什么只能对示例图像有效
  • ¥100 连续两帧图像高速减法
  • ¥15 组策略中的计算机配置策略无法下发
  • ¥15 如何绘制动力学系统的相图
  • ¥15 对接wps接口实现获取元数据
  • ¥20 给自己本科IT专业毕业的妹m找个实习工作
  • ¥15 用友U8:向一个无法连接的网络尝试了一个套接字操作,如何解决?
  • ¥30 我的代码按理说完成了模型的搭建、训练、验证测试等工作(标签-网络|关键词-变化检测)