Reputation: 27
I just recently started learning go and wanted to test my skills by writing a program that calculates all amicable and perfect numbers. Sadly I'm having issues with my code! If anyone knows how I can stop the program after all numbers are calculated, preferably without an error, please let me know.
package main
import (
"fmt"
"strings"
)
func getDivisorSum(number int) int {
divisors := 0
for possibleDivisor := 1; possibleDivisor <= number / 2; possibleDivisor++ {
if number % possibleDivisor == 0 {
divisors += possibleDivisor
}
}
return divisors
}
func checkNumber(number int, channel chan string, done *int) {
first := getDivisorSum(number)
if first == number {
channel <- fmt.Sprintf("%d", number)
*done += 1
return
}
second := getDivisorSum(first)
if number == second {
channel <- fmt.Sprintf("%d:%d", number, first)
*done += 1
return
}
}
func checkNumberRange(min int, max int) {
channel := make(chan string)
done := 0
for number := min; number <= max; number++ {
go checkNumber(number, channel, &done)
}
for {
tmp := <- channel
if strings.Contains(tmp, ":") {
parts := strings.Split(tmp, ":")
fmt.Printf("%s is an amicable of %s!\n", parts[0], parts[1])
} else {
fmt.Printf("%s is perfect!\n", tmp)
}
}
}
func main() {
checkNumberRange(1, 65536)
}
At the moment the program is crashing with a deadlock at
tmp := <- channel
Thanks in advance, Jooarye!
Upvotes: 1
Views: 662
Reputation: 1201
Reading from a channel is blocked until there is data. You can check the second return value to assert whether the channel was closed.
Use close(channel)
when finished writing.
tmp, ok := <-channel
You have an endless for-loop forever reading the channel.
Upvotes: -1
Reputation: 51572
There are multiple problems with your program:
done
is racy. Multiple goroutines are writing to it without synchronization. It doesn't look like it's been used.One way to deal with this is to use a sync.WaitGroup, and keep track of starting/stopping goroutines that way.
wg:=sync.WaitGroup{}
for number := min; number <= max; number++ {
wg.Add(1)
go checkNumber(number, channel, &wg)
}
In checkNumber:
func checkNumber(number int, channel chan string,wg *wg.WaitGroup) {
defer wg.Done()
...
}
You can have a separate goroutine that waits for all goroutines to complete:
go func() {
wg.Wait()
close(chan)
}()
And, in your for-loop, read it like this, so it terminates when the channel closes:
for tmp:=range chan {
...
}
Upvotes: 7
Reputation: 66
I advise you to read the following article https://blog.golang.org/pipelines.
But I don't think that channels are a good choice in this case. Perhaps it would be better to use sync.WaitGroup
wg := &sync.WaitGroup{}
wg.Add(max)
for number := min; number <= max; number++ {
go func(number int) {
defer wg.Done()
res := checkNumber(number)
if strings.Contains(res, ":") {
parts := strings.Split(res, ":")
fmt.Printf("%s is an amicable of %s!\n", parts[0], parts[1])
} else {
fmt.Printf("%s is perfect!\n", res)
}
}(number)
}
wg.Wait()
Upvotes: 0