douliao8318 2016-08-08 14:58
浏览 86
已采纳

Goroutines共享一个数组通道:试图解决数据竞争

I try to write a complex program with parallel goroutines. It is my first program with channels ;) Each goroutine returns an array and, unfortunately, the result is "random". If I run 10 times the program, I have 10 different results :(

This is an over simplification of my program, the results is good (maybe because it is too simple) but when I run it with -race argument, there is 4 data races.

I tried to had a close() function but it did not worked.

May you help me to find the mistake ? Thank you very much in advance !

package main

import "fmt"
import "sync"
import "strconv"


func cat_strings(a int, b string) []string{
    var y []string

    j := strconv.Itoa(a)
    y = append(y, j)
    y = append(y, b)
    return y
}

func main() {
    var slice []string
    var wg sync.WaitGroup
    var x []string

    queue := make(chan []string, 10)

    wg.Add(10)
    for i := 0; i < 10; i++ {
        go func(i int) {
            defer wg.Done()
            x = cat_strings(i, "var")
            queue <- x
        }(i)

    }
    //close(queue)

    go func() {
        defer wg.Done()
        for t := range queue {
            slice = append(slice, t...)
        }
    }()

    wg.Wait()
    fmt.Println(slice)
}
  • 写回答

1条回答 默认 最新

  • dreamer1231 2016-08-08 15:11
    关注

    There's two pieces to this fix, don't share the slices between goroutines, and then range over queue synchronously in main.

    import (
        "fmt"
        "strconv"
        "sync"
    )
    
    func cat_strings(a int, b string) []string {
        var y []string
    
        j := strconv.Itoa(a)
        y = append(y, j)
        y = append(y, b)
        return y
    }
    
    func main() {
        var slice []string
        var wg sync.WaitGroup
    
        queue := make(chan []string, 10)
    
        wg.Add(10)
        for i := 0; i < 10; i++ {
            go func(i int) {
                defer wg.Done()
                queue <- cat_strings(i, "var")
            }(i)
    
        }
    
        go func() {
            wg.Wait()
            close(queue)
        }()
    
        for t := range queue {
            slice = append(slice, t...)
        }
    
        fmt.Println(slice)
    }
    

    There's no reason for the extra x slice you're sharing between goroutines. If each goroutine needs another slice, define a new one for each. Sharing a single slice is always going to require extra synchronization.

    The other race is between the goruoutine appending from queue to the slice slice, and the final fmt.Println. There's no reason for those be concurrent since you don't want to print until all values have been read, so finish the for-range loop entirely before printing the final value.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?