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条)

报告相同问题?

悬赏问题

  • ¥15 Llama如何调用shell或者Python
  • ¥20 谁能帮我挨个解读这个php语言编的代码什么意思?
  • ¥15 win10权限管理,限制普通用户使用删除功能
  • ¥15 minnio内存占用过大,内存没被回收(Windows环境)
  • ¥65 抖音咸鱼付款链接转码支付宝
  • ¥15 ubuntu22.04上安装ursim-3.15.8.106339遇到的问题
  • ¥15 blast算法(相关搜索:数据库)
  • ¥15 请问有人会紧聚焦相关的matlab知识嘛?
  • ¥15 网络通信安全解决方案
  • ¥50 yalmip+Gurobi