dpp66953 2017-05-24 08:27
浏览 18
已采纳

追加不是线程安全的吗?

I noticed that if I tried appending to a slice using goroutines inside a for loop, there would be instances where I would get missing/blank data:

destSlice := make([]myClass, 0)

var wg sync.WaitGroup
for _, myObject := range sourceSlice {
    wg.Add(1)
    go func(closureMyObject myClass) {
        defer wg.Done()
        var tmpObj myClass
        tmpObj.AttributeName = closureMyObject.AttributeName
        destSlice = append(destSlice, tmpObj)
    }(myObject)
}
wg.Wait()

Sometimes, when I print all AttributeNames from destSlice, some elements are empty strings (""), and other times, some elements from sourceSlice are not present in destSlice.

Does my code have a data race, and does this mean that append is not thread-safe for concurrent use by multiple goroutines?

  • 写回答

2条回答 默认 最新

  • dongpai2754 2017-05-24 08:32
    关注

    In Go no value is safe for concurrent read/write, slices (which are slice headers) are no exception.

    Yes, your code has data races. Run with the -race option to verify.

    Example:

    type myClass struct {
        AttributeName string
    }
    sourceSlice := make([]myClass, 100)
    
    destSlice := make([]myClass, 0)
    
    var wg sync.WaitGroup
    for _, myObject := range sourceSlice {
        wg.Add(1)
        go func(closureMyObject myClass) {
            defer wg.Done()
            var tmpObj myClass
            tmpObj.AttributeName = closureMyObject.AttributeName
            destSlice = append(destSlice, tmpObj)
        }(myObject)
    }
    wg.Wait()
    

    Running it with

    go run -race play.go
    

    Output is:

    ==================
    WARNING: DATA RACE
    Read at 0x00c420074000 by goroutine 6:
      main.main.func1()
          /home/icza/gows/src/play/play.go:20 +0x69
    
    Previous write at 0x00c420074000 by goroutine 5:
      main.main.func1()
          /home/icza/gows/src/play/play.go:20 +0x106
    
    Goroutine 6 (running) created at:
      main.main()
          /home/icza/gows/src/play/play.go:21 +0x1cb
    
    Goroutine 5 (running) created at:
      main.main()
          /home/icza/gows/src/play/play.go:21 +0x1cb
    ==================
    ==================
    WARNING: DATA RACE
    Read at 0x00c42007e000 by goroutine 6:
      runtime.growslice()
          /usr/local/go/src/runtime/slice.go:82 +0x0
      main.main.func1()
          /home/icza/gows/src/play/play.go:20 +0x1a7
    
    Previous write at 0x00c42007e000 by goroutine 5:
      main.main.func1()
          /home/icza/gows/src/play/play.go:20 +0xc4
    
    Goroutine 6 (running) created at:
      main.main()
          /home/icza/gows/src/play/play.go:21 +0x1cb
    
    Goroutine 5 (running) created at:
      main.main()
          /home/icza/gows/src/play/play.go:21 +0x1cb
    ==================
    ==================
    WARNING: DATA RACE
    Write at 0x00c420098120 by goroutine 80:
      main.main.func1()
          /home/icza/gows/src/play/play.go:20 +0xc4
    
    Previous write at 0x00c420098120 by goroutine 70:
      main.main.func1()
          /home/icza/gows/src/play/play.go:20 +0xc4
    
    Goroutine 80 (running) created at:
      main.main()
          /home/icza/gows/src/play/play.go:21 +0x1cb
    
    Goroutine 70 (running) created at:
      main.main()
          /home/icza/gows/src/play/play.go:21 +0x1cb
    ==================
    Found 3 data race(s)
    exit status 66
    

    Solution is simple, use a sync.Mutex to protect writing the destSlice value:

    destSlice := make([]myClass, 0)
    mux := &sync.Mutex{}
    
    var wg sync.WaitGroup
    for _, myObject := range sourceSlice {
        wg.Add(1)
        go func(closureMyObject myClass) {
            defer wg.Done()
            var tmpObj myClass
            tmpObj.AttributeName = closureMyObject.AttributeName
            mux.Lock()
            destSlice = append(destSlice, tmpObj)
            mux.Unlock()
        }(myObject)
    }
    wg.Wait()
    

    You could also solve it in other ways, e.g. you could use a channel on which you'd send the value to be appended, and have a designated goroutine receiving from this channel and do the append.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥20 如何通过代码传输视频到亚马逊平台
  • ¥15 php查询mysql数据库并显示至下拉列表中
  • ¥15 freertos下使用外部中断失效
  • ¥15 输入的char字符转为int类型,不是对应的ascall码,如何才能使之转换为对应ascall码?或者使输入的char字符可以正常与其他字符比较?
  • ¥15 devserver配置完 启动服务 无法访问static上的资源
  • ¥15 解决websocket跟c#客户端通信
  • ¥30 Python调用dll文件输出Nan重置dll状态
  • ¥15 浮动div的高度控制问题。
  • ¥66 换电脑后应用程序报错
  • ¥50 array数据同步问题