Goroutines sharing an array channel : trying to so

2019-06-14 05:48发布

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条回答
干净又极端
2楼-- · 2019-06-14 06:08

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.

查看更多
登录 后发表回答