尽管存在WaitGroup,但Goroutines似乎被中断了。

huangapple go评论85阅读模式
英文:

Goroutines seem to be interrupted despite the presence of a WaitGroup

问题

我有一个问题,尽管使用了WaitGroup,但goroutine没有结束。在附加的代码中,您可以看到对排列的Heap算法的实现。我想加快速度,所以我为每个可能的第一个数字创建了一个goroutine,从而将每个goroutine的排列减少到(n-1)!。总共,我应该仍然有n!个排列(n*(n-1)! = n!),但是我的主程序似乎在子程序完成之前退出了。然后,我尝试跟踪执行的排列。与我的信念相反,执行的排列数量并不是恒定的,而是总是略微(对于较小的n)或非常(对于较大的n)小于n!

例如,n=4的排列每次都是24,即4!,因此所有的goroutine都已经完成。如果我有一个更大的数字,比如n=8,我得到的值大约是13500,而不是预期的40000 = 8!

这种行为是从哪里来的?如何确保在主程序退出之前,所有的goroutine都已经完成?

package main

import (
	"fmt"
	"sync"
)

var wg sync.WaitGroup
var permutations int

func main() {

	n := 9

	wg.Add(n)

	for i := 0; i < n; i++ {

		var arr []int
		for j := 0; j < n; j++ {
			if i != j {
				arr = append(arr, j+1)
			}
		}
		go threadFunction(n-1, i+1, arr)
	}

	wg.Wait()

	fmt.Println(permutations)
}

func threadFunction(k int, suffix int, arr []int) {

	defer wg.Done()

	heapPermutation(k, suffix, arr)
}

func heapPermutation(k int, prefix int, arr []int) {

	if k == 1 {
		arr = append(arr, prefix)
		// fmt.Println(arr)
		permutations++
	} else {
		heapPermutation(k-1, prefix, arr)

		for i := 0; i < k-1; i++ {
			if k%2 == 0 {
				arr[i], arr[k-1] = arr[k-1], arr[i]
			} else {
				arr[0], arr[k-1] = arr[k-1], arr[0]
			}
			heapPermutation(k-1, prefix, arr)
		}
	}
}

(在https://go.dev/play/上也可以很容易地实现相同的行为,因此它是可以重现的。)

英文:

I have a problem with goroutines not concluding in spite of a WaitGroup. In the code appended, you can see an implementation of Heap's algorithm for permutations. I wanted to speed it up, and so I created a goroutine for every possible first number and thereby decreased the permutations per goroutine to (n-1)!. In total, I should still have n! permutations (n*(n-1)! = n!), but my main routine seems to exit before the sub routines are finished. I then tried tracking the executed permutations. Against my belief, the number of permutations executed isn't constant, but always a bit (for low n) or very much (for large n) under n!.

For example, the permutations for n=4 is 24 every time, that is 4! and thereby all goroutines have finished. If I have a higher number like n=8, I get a value around 13500 and not the expected 40000 = 8!.

Where does this behavior come from? How can I ensure that all my goroutines have finished before the main program exits?

package main

import (
	&quot;fmt&quot;
	&quot;sync&quot;
)

var wg sync.WaitGroup
var permutations int

func main() {

	n := 9

	wg.Add(n)

	for i := 0; i &lt; n; i++ {

		var arr []int
		for j := 0; j &lt; n; j++ {
			if i != j {
				arr = append(arr, j+1)
			}
		}
		go threadFunction(n-1, i+1, arr)
	}

	wg.Wait()

	fmt.Println(permutations)
}

func threadFunction(k int, suffix int, arr []int) {

	defer wg.Done()

	heapPermutation(k, suffix, arr)
}

func heapPermutation(k int, prefix int, arr []int) {

	if k == 1 {
		arr = append(arr, prefix)
		// fmt.Println(arr)
		permutations++
	} else {
		heapPermutation(k-1, prefix, arr)

		for i := 0; i &lt; k-1; i++ {
			if k%2 == 0 {
				arr[i], arr[k-1] = arr[k-1], arr[i]
			} else {
				arr[0], arr[k-1] = arr[k-1], arr[0]
			}
			heapPermutation(k-1, prefix, arr)
		}
	}
}

(The same behavior can be easily achieved, e.g. on https://go.dev/play/, thus it is very much reproducible.)

答案1

得分: 2

在你的代码中,goroutine 并发访问了 permutations 变量。当你增加 n 的值时,工作负载增加,这导致了意外结果的问题。

你可以使用一个 mutex,它可以确保只有一个 goroutine 可以同时访问 permutations

package main

import (
	"fmt"
	"sync"
)

var wg sync.WaitGroup
var permutations int
var permutationsMutex sync.Mutex

func main() {

	n := 9

	wg.Add(n)

	for i := 0; i < n; i++ {

		var arr []int
		for j := 0; j < n; j++ {
			if i != j {
				arr = append(arr, j+1)
			}
		}
		go threadFunction(n-1, i+1, arr)
	}

	wg.Wait()

	fmt.Println(permutations)
}

func threadFunction(k int, suffix int, arr []int) {

	defer wg.Done()

	heapPermutation(k, suffix, arr)
}

func heapPermutation(k int, prefix int, arr []int) {

	if k == 1 {
		arr = append(arr, prefix)
		permutationsMutex.Lock()
		permutations++
		permutationsMutex.Unlock()
	} else {
		heapPermutation(k-1, prefix, arr)

		for i := 0; i < k-1; i++ {
			if k%2 == 0 {
				arr[i], arr[k-1] = arr[k-1], arr[i]
			} else {
				arr[0], arr[k-1] = arr[k-1], arr[0]
			}
			heapPermutation(k-1, prefix, arr)
		}
	}
}

希望对你有帮助!

英文:

In your code, goroutines access permutations variable concurrently. When you increase value of n, the workload increases and it becomes the issue leading to an unexpected result.

You can use a mutex, it will ensure that only one goroutine can access the permutations at the time.

package main
import (
&quot;fmt&quot;
&quot;sync&quot;
)
var wg sync.WaitGroup
var permutations int
var permutationsMutex sync.Mutex
func main() {
n := 9
wg.Add(n)
for i := 0; i &lt; n; i++ {
var arr []int
for j := 0; j &lt; n; j++ {
if i != j {
arr = append(arr, j+1)
}
}
go threadFunction(n-1, i+1, arr)
}
wg.Wait()
fmt.Println(permutations)
}
func threadFunction(k int, suffix int, arr []int) {
defer wg.Done()
heapPermutation(k, suffix, arr)
}
func heapPermutation(k int, prefix int, arr []int) {
if k == 1 {
arr = append(arr, prefix)
permutationsMutex.Lock()
permutations++
permutationsMutex.Unlock()
} else {
heapPermutation(k-1, prefix, arr)
for i := 0; i &lt; k-1; i++ {
if k%2 == 0 {
arr[i], arr[k-1] = arr[k-1], arr[i]
} else {
arr[0], arr[k-1] = arr[k-1], arr[0]
}
heapPermutation(k-1, prefix, arr)
}
}
}

huangapple
  • 本文由 发表于 2023年4月12日 23:53:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/75997538.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定