goroutine中的代码块会产生奇怪的错误结果

时间:2013-12-04 06:39:31

标签: go goroutine

我有一个很大的N * 1名字阵列
我目前正在使用goroutine计算名称之间的编辑距离

问题是[B] [C]的结果不同,也许就像

ABC BCD 7
ABC BCD 3

名称中有20000条记录

var names []string

将名称划分为两个块

nameCount := len(names)  
procs := 2  
chunkSize := nameCount / procs 

频道

ch := make(chan int)  
var wg sync.WaitGroup


for i := 0; i < procs; i++ { //create two goroutines
    start := i * chunkSize
    end := (i+1)*chunkSize - 1
    fmt.Println(start, end) //get slice start and end
    wg.Add(1)
    go func(slices []string, allnames []string) {
        for _, slice := range slices {
            minDistance = 256
            distance := 0
            sum := 0
            for _, name := range allnames {
                distance = calcEditDist(slice, name) //get the LD [A]
                sum += 1
                if distance > 0 && distance < minDistance {
                    minDistance = distance
                    fmt.Println(slice, name, distance) //[B]
                    fmt.Println(slice, name, calcEditDist(slice, name)) //[C]
                } else if distance == minDistance {
                    fmt.Println(slice, name, distance)
                    fmt.Println(slice, name, calcEditDist(slice, name))
                }
            }
            // for _, name := range allnames {
            //  fmt.Println(slice, name)
            // }
            ch <- sum
            // fmt.Println(len(allnames), slice)
            break
        }
        wg.Done()
    }(names[start:end], names)
}

我放置了calcEditDist @ https://github.com/copywrite/keyboardDistance/blob/master/parallel.go

PS:
如果我宣布

var dp [max][max]int
在calcEditDist中作为局部变量而不是全局变量,结果是正确的,但速度非常慢

更新1
感谢所有伙伴,我在下面分三步采取了很好的建议 1)我将dp缩小到一个非常合理的尺寸,如100或甚至更小,DONE
2)我把dp声明放在每个goroutine中并传递它的指针,就像Nick说的那样,DONE
3)稍后我将尝试动态分配dp,LATER

性能急剧提升,╰(°▽°)╯

1 个答案:

答案 0 :(得分:1)

正如您在帖子中指出的那样,将dp作为全局变量是问题所在。

每次在CalcEditDistance分配它都太慢了。

您有两种可能的解决方案。

1)每个go例程只需要1 dp个数组,所以在for循环中分配它并传递一个指针(不要直接传递数组,因为数组会传递值,这将涉及到很多复制!)

for i := 0; i < procs; i++ { //create two goroutines
    start := i * chunkSize
    end := (i+1)*chunkSize - 1
    fmt.Println(start, end) //get slice start and end
    wg.Add(1)
    go func(slices []string, allnames []string) {
        var dp [max][max]int // allocate
        for _, slice := range slices {
            minDistance = 256
            distance := 0
            sum := 0
            for _, name := range allnames {
                distance = calcEditDist(slice, name, &dp) // pass dp pointer here

更改calcEditDist以获取dp

func CalcEditDist(A string, B string, dp *[max][max]int) int {
        lenA := len(A)
        lenB := len(B)

2)重新编写calcEditDistance,这样就不需要大量的O(N ^ 2)dp数组了。

如果你仔细研究这个函数,它只能访问一行和左边的一列,所以你实际需要的所有存储都是前一行和前一列,你可以用很少的成本动态分配。这也可以使它扩展到任何长度的字符串。

虽然需要一点仔细考虑!