Go-lang newbie here. I am trying out Go's Tour of Go, and came across an exercise about channels (https://tour.golang.org/concurrency/7).
The idea is to walk two trees and then evaluate if the trees are equivalent.
I wanted to solve this exercise using a select waiting for results from both channels. When both would finish I evaluate the resulting slice. Unfortunately the method goes on an infinite loop. I added some output to see what was happening and noticed that only one of the channels was being closed, and then opened again.
I am clearly doing something wrong, but I can't see what.
My question is what am I doing wrong? What assumption am I making regarding the closing of channels that makes the code below go into an infinite loop?
package main
import (
"golang.org/x/tour/tree"
"fmt"
)
// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, ch chan int) {
_walk(t, ch)
close(ch)
}
func _walk(t *tree.Tree, ch chan int) {
if (t.Left != nil) {
_walk(t.Left, ch)
}
ch <- t.Value
if (t.Right != nil) {
_walk(t.Right, ch)
}
}
// Same determines whether the trees
// t1 and t2 contain the same values.
func Same(t1, t2 *tree.Tree) bool {
ch1 := make(chan int)
ch2 := make(chan int)
go Walk(t1, ch1)
go Walk(t2, ch2)
var out1 []int
var out2 []int
var tree1open, tree2open bool
var tree1val, tree2val int
for {
select {
case tree1val, tree1open = <- ch1:
out1 = append(out1, tree1val)
case tree2val, tree2open = <- ch2:
out2 = append(out2, tree2val)
default:
if (!tree1open && !tree2open) {
break
} else {
fmt.Println("Channel open?", tree1open, tree2open)
}
}
}
if (len(out1) != len(out2)) {
return false
}
for i := 0 ; i < len(out1) ; i++ {
if (out1[i] != out2[i]) {
return false
}
}
return true
}
func main() {
ch := make(chan int)
go Walk(tree.New(1), ch)
for i := range ch {
fmt.Println(i)
}
fmt.Println(Same(tree.New(1), tree.New(1)))
fmt.Println(Same(tree.New(1), tree.New(2)))
}
A "break" statement terminates execution of the innermost "for", "switch" or "select" statement.
see: http://golang.org/ref/spec#Break_statements
the break statement in your example terminates the select statement, the "innermost" statement.
so add label: ForLoop before for loop and add break ForLoop
ForLoop:
for {
select {
case tree1val, tree1open = <-ch1:
if tree1open {
out1 = append(out1, tree1val)
} else if !tree2open {
break ForLoop
}
case tree2val, tree2open = <-ch2:
if tree2open {
out2 = append(out2, tree2val)
} else if !tree1open {
break ForLoop
}
}
}
don't read the rest if you want to solve that problem yourself, and come back when you are done:
solution 1 (similar to yours):
package main
import "fmt"
import "golang.org/x/tour/tree"
// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, ch chan int) {
_walk(t, ch)
close(ch)
}
func _walk(t *tree.Tree, ch chan int) {
if t.Left != nil {
_walk(t.Left, ch)
}
ch <- t.Value
if t.Right != nil {
_walk(t.Right, ch)
}
}
// Same determines whether the trees
// t1 and t2 contain the same values.
func Same(t1, t2 *tree.Tree) bool {
ch1, ch2 := make(chan int), make(chan int)
go Walk(t1, ch1)
go Walk(t2, ch2)
tree1open, tree2open := false, false
tree1val, tree2val := 0, 0
out1, out2 := make([]int, 0, 10), make([]int, 0, 10)
ForLoop:
for {
select {
case tree1val, tree1open = <-ch1:
if tree1open {
out1 = append(out1, tree1val)
} else if !tree2open {
break ForLoop
}
case tree2val, tree2open = <-ch2:
if tree2open {
out2 = append(out2, tree2val)
} else if !tree1open {
break ForLoop
}
}
}
if len(out1) != len(out2) {
return false
}
for i, v := range out1 {
if v != out2[i] {
return false
}
}
return true
}
func main() {
ch := make(chan int)
go Walk(tree.New(1), ch)
for i := range ch {
fmt.Println(i)
}
fmt.Println(Same(tree.New(1), tree.New(1)))
fmt.Println(Same(tree.New(1), tree.New(2)))
}
output:
1
2
3
4
5
6
7
8
9
10
true
false
another way:
package main
import "fmt"
import "golang.org/x/tour/tree"
// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, ch chan int) {
_walk(t, ch)
close(ch)
}
func _walk(t *tree.Tree, ch chan int) {
if t != nil {
_walk(t.Left, ch)
ch <- t.Value
_walk(t.Right, ch)
}
}
// Same determines whether the trees
// t1 and t2 contain the same values.
func Same(t1, t2 *tree.Tree) bool {
ch1, ch2 := make(chan int), make(chan int)
go Walk(t1, ch1)
go Walk(t2, ch2)
for v := range ch1 {
if v != <-ch2 {
return false
}
}
return true
}
func main() {
ch := make(chan int)
go Walk(tree.New(1), ch)
for v := range ch {
fmt.Println(v)
}
fmt.Println(Same(tree.New(1), tree.New(1)))
fmt.Println(Same(tree.New(1), tree.New(2)))
}
output:
1
2
3
4
5
6
7
8
9
10
true
false
and see:
Go Tour Exercise: Equivalent Binary Trees
The suggestion by Amd is a valid one in the previous answer. However, looking at the problem you're trying to solve, it still does not solve the it. (If you run the program, it will output true for both the cases)
Here's the problem:
for {
select {
case tree1val, tree1open = <-ch1:
out1 = append(out1, tree1val)
case tree2val, tree2open = <-ch2:
out2 = append(out2, tree2val)
default:
//runtime.Gosched()
if !tree1open && !tree2open {
break ForLoop
} else {
fmt.Println("Channel open?", tree1open, tree2open)
}
}
}
In this case, since the default values of tree1open and tree2open are false (according to golang specification), it goes to the 'default' case, because select is non-blocking, and simply breaks from the ForLoop, without even filling the out1 and out2 slices (possibly, since these are goroutines). Hence the lengths of out1 and out2 remain zero, due to which it outputs true in most cases.
Here is the correction:
ForLoop:
for {
select {
case tree1val, tree1open = <-ch1:
if tree1open {
out1 = append(out1, tree1val)
}
if !tree1open && !tree2open {
break ForLoop
}
case tree2val, tree2open = <-ch2:
if tree2open {
out2 = append(out2, tree2val)
}
if !tree1open && !tree2open {
break ForLoop
}
default:
}
}
The key thing to note is that we have to check whether the channels have been closed in both cases (equivalent to saying whether tree1open and tree2open are both false). Here, it will correctly fill up out1 and out2 slices and then further compare their respective values.
The check for tree1open (or tree2open) being true has been added before append simply to avoid appending zero values to out1 (or out2).
Thanks,
Related
I have 3 merge sort implementations:
MergeSort: simple one without concurrency;
MergeSortSmart: with concurrency limited by buffered channel size limit. If buffer is full, calls the simple implementation;
MergeSortSmartBug: same strategy as the previous one, but with a small "refactor", passing wg pointer to a function reducing code duplication.
The first two works as expected, but the third one returns an empty slice instead of the sorted input. I couldn't understand what happened and found no answers as well.
Here is the playground link for the code: https://play.golang.org/p/DU1ypbanpVi
package main
import (
"fmt"
"math/rand"
"runtime"
"sync"
)
type pass struct{}
var semaphore = make(chan pass, runtime.NumCPU())
func main() {
rand.Seed(10)
s := make([]int, 16)
for i := 0; i < 16; i++ {
s[i] = int(rand.Int31n(1000))
}
fmt.Println(s)
fmt.Println(MergeSort(s))
fmt.Println(MergeSortSmart(s))
fmt.Println(MergeSortSmartBug(s))
}
func merge(l, r []int) []int {
tmp := make([]int, 0, len(l)+len(r))
for len(l) > 0 || len(r) > 0 {
if len(l) == 0 {
return append(tmp, r...)
}
if len(r) == 0 {
return append(tmp, l...)
}
if l[0] <= r[0] {
tmp = append(tmp, l[0])
l = l[1:]
} else {
tmp = append(tmp, r[0])
r = r[1:]
}
}
return tmp
}
func MergeSort(s []int) []int {
if len(s) <= 1 {
return s
}
n := len(s) / 2
l := MergeSort(s[:n])
r := MergeSort(s[n:])
return merge(l, r)
}
func MergeSortSmart(s []int) []int {
if len(s) <= 1 {
return s
}
n := len(s) / 2
var wg sync.WaitGroup
wg.Add(2)
var l, r []int
select {
case semaphore <- pass{}:
go func() {
l = MergeSortSmart(s[:n])
<-semaphore
wg.Done()
}()
default:
l = MergeSort(s[:n])
wg.Done()
}
select {
case semaphore <- pass{}:
go func() {
r = MergeSortSmart(s[n:])
<-semaphore
wg.Done()
}()
default:
r = MergeSort(s[n:])
wg.Done()
}
wg.Wait()
return merge(l, r)
}
func MergeSortSmartBug(s []int) []int {
if len(s) <= 1 {
return s
}
n := len(s) / 2
var wg sync.WaitGroup
wg.Add(2)
l := mergeSmart(s[:n], &wg)
r := mergeSmart(s[n:], &wg)
wg.Wait()
return merge(l, r)
}
func mergeSmart(s []int, wg *sync.WaitGroup) []int {
var tmp []int
select {
case semaphore <- pass{}:
go func() {
tmp = MergeSortSmartBug(s)
<-semaphore
wg.Done()
}()
default:
tmp = MergeSort(s)
wg.Done()
}
return tmp
}
Why does the Bug version returns an empty slice? How can I refactor the Smart version without doing two selects one after the other?
Sorry for I couldn't reproduce this behavior in a smaller example.
The problem is not with the WaitGroup itself. It's with your concurrency handling. Your mergeSmart function lunches a go routine and returns the tmp variable without waiting for the go routine to finish.
You might want to try a pattern more like this:
leftchan := make(chan []int)
rightchan := make(chan []int)
go mergeSmart(s[:n], leftchan)
go mergeSmart(s[n:], rightchan)
l := <-leftchan
r := <-rightchan
Or you can use a single channel if order doesn't matter.
mergeSmart doesn't wait on the wg, so it returns a tmp that hasn't received a value yet. You could probably repair it by passing a reference to the destination slice in to the function, instead of returning a slice.
Look at the mergeSmart function. When the select enter into the first case, the goroutine is launched and imediatly returns tmp (which is an empty array). In that case there is no way to get the right value. (See advanced debugging prints here https://play.golang.org/p/IedaY3muso2)
Maybe passing arrays preallocated by reference?
I implemented both suggestions (passing slice by reference and using channels) and the (working!) result is here: https://play.golang.org/p/DcDC_-NjjAH
package main
import (
"fmt"
"math/rand"
"runtime"
"sync"
)
type pass struct{}
var semaphore = make(chan pass, runtime.NumCPU())
func main() {
rand.Seed(10)
s := make([]int, 16)
for i := 0; i < 16; i++ {
s[i] = int(rand.Int31n(1000))
}
fmt.Println(s)
fmt.Println(MergeSort(s))
fmt.Println(MergeSortSmart(s))
fmt.Println(MergeSortSmartPointer(s))
fmt.Println(MergeSortSmartChan(s))
}
func merge(l, r []int) []int {
tmp := make([]int, 0, len(l)+len(r))
for len(l) > 0 || len(r) > 0 {
if len(l) == 0 {
return append(tmp, r...)
}
if len(r) == 0 {
return append(tmp, l...)
}
if l[0] <= r[0] {
tmp = append(tmp, l[0])
l = l[1:]
} else {
tmp = append(tmp, r[0])
r = r[1:]
}
}
return tmp
}
func MergeSort(s []int) []int {
if len(s) <= 1 {
return s
}
n := len(s) / 2
l := MergeSort(s[:n])
r := MergeSort(s[n:])
return merge(l, r)
}
func MergeSortSmart(s []int) []int {
if len(s) <= 1 {
return s
}
n := len(s) / 2
var wg sync.WaitGroup
wg.Add(2)
var l, r []int
select {
case semaphore <- pass{}:
go func() {
l = MergeSortSmart(s[:n])
<-semaphore
wg.Done()
}()
default:
l = MergeSort(s[:n])
wg.Done()
}
select {
case semaphore <- pass{}:
go func() {
r = MergeSortSmart(s[n:])
<-semaphore
wg.Done()
}()
default:
r = MergeSort(s[n:])
wg.Done()
}
wg.Wait()
return merge(l, r)
}
func MergeSortSmartPointer(s []int) []int {
if len(s) <= 1 {
return s
}
n := len(s) / 2
var l, r []int
var wg sync.WaitGroup
wg.Add(2)
mergeSmartPointer(&l, s[:n], &wg)
mergeSmartPointer(&r, s[n:], &wg)
wg.Wait()
return merge(l, r)
}
func mergeSmartPointer(tmp *[]int, s []int, wg *sync.WaitGroup) {
select {
case semaphore <- pass{}:
go func() {
*tmp = MergeSortSmartPointer(s)
<-semaphore
wg.Done()
}()
default:
*tmp = MergeSort(s)
wg.Done()
}
}
func MergeSortSmartChan(s []int) []int {
if len(s) <= 1 {
return s
}
n := len(s) / 2
lchan := make(chan []int)
rchan := make(chan []int)
go mergeSmartChan(s[:n], lchan)
go mergeSmartChan(s[n:], rchan)
l := <-lchan
r := <-rchan
return merge(l, r)
}
func mergeSmartChan(s []int, c chan []int) {
select {
case semaphore <- pass{}:
go func() {
c <- MergeSortSmartChan(s)
<-semaphore
}()
default:
c <- MergeSort(s)
}
}
I understood 100% what I was doing wrong, thanks!
And for future references, here's the benchmark of sorting a slice of 100,000 elems:
$ go test -bench=.
goos: linux
goarch: amd64
cpu: Intel(R) Core(TM) i5-9300H CPU # 2.40GHz
BenchmarkMergeSort-8 97 12230309 ns/op
BenchmarkMergeSortSmart-8 181 7209844 ns/op
BenchmarkMergeSortSmartPointer-8 163 7483136 ns/op
BenchmarkMergeSortSmartChan-8 156 8149585 ns/op
In this scenario I have 3x boolean vars (already set to either true or false)
The goal here is to identify if more than one of the set of bool vars are set to true
Right now I have the following written, which does work:
boolval := 0
if *firstbool {
boolval++
}
if *secondbool {
boolval++
}
if *thirdbool {
boolval++
}
if boolval > 1 {
// More than 1 of the bool vars are true
}
I always flag my logic if I am writing successive if{} statements, so I figured I would ask you geniuses how you would accomplish this.
identify if more than one of the set of bool vars are set to true
For example, write a set function:
package main
import "fmt"
func nTrue(b ...bool) int {
n := 0
for _, v := range b {
if v {
n++
}
}
return n
}
func main() {
v1, v2, v3 := true, false, true
fmt.Println(nTrue(v1, v2, v3))
if nTrue(v1, v2, v3) > 1 {
// . . .
}
// Or
if n := nTrue(v1, v2, v3); n > 1 {
// . . .
}
// Or
n := nTrue(v1, v2, v3)
if n > 1 {
// . . .
}
}
Playground: https://play.golang.org/p/g3WCN6BgGly
Output:
2
For example, range over the set,
package main
import "fmt"
func main() {
v1, v2, v3 := true, false, true
boolval := 0
for _, v := range []bool{v1, v2, v3} {
if v {
boolval++
}
}
if boolval > 1 {
// . . .
}
fmt.Println(boolval > 1, boolval)
}
Playground: https://play.golang.org/p/R6UGb8YYEFw
Output:
true 2
Depending on how you're getting the values of firstbool et. al, there might be a more idiomatic approach you should take here. Consider that channels and goroutines can handle doing the accounting in the background. If your booleans are the result of some heavier operations, it might make sense to do something like:
package main
import "fmt"
func firstFunc(ch chan bool) {
// assume this works, true!
ch <- true
}
func secondFunc(ch chan bool) {
// assume this fails, false!
ch <- false
}
func thirdFunc(ch chan bool) {
// assume this works, true!
ch <- true
}
func listener(ch chan bool, numCallers int) <-chan bool {
outch := make(chan bool)
THRESHOLD := 2
go func(outch chan<- bool) {
count := 0
trues := 0
for count < numCallers && trues < THRESHOLD {
val := <-ch
if val {
trues++
}
count++
}
outch <- trues >= THRESHOLD
}(outch)
return outch
}
func main() {
ch := make(chan bool)
resultch := listener(ch, 3)
go firstFunc(ch)
go secondFunc(ch)
go thirdFunc(ch)
if <-resultch {
fmt.Println("Two or more processes succeeded")
}
}
However this is way over-engineered for a simple need, so consider this only if this pattern fits the larger design of your application.
The attached gist is a simple program using channels in a producer / multi-consumer model. For some reason,
go run channels.go prints all the results but does not return (and does not deadlock or at least go doesn't give me that panic that a deadlock occurs.)
type walkietalkie struct {
in chan int
out chan int
quit chan bool
}
var items []int = []int{
0, 1, 2, 3, 4, 5,
}
func work1(q walkietalkie) {
for {
select {
case a, more := <- q.in:
if more {
q.out <- a * 2
}
default:
break
}
}
}
func work2(q walkietalkie) {
for {
select {
case a, more := <- q.in:
if more {
q.out <- a * -1
}
default:
break
}
}
}
func work3(q walkietalkie) {
for {
select {
case a, more := <- q.in:
if more {
q.out <- a * 7
}
default:
break
}
}
}
func main() {
results := make(chan int, 18)
defer close(results)
w := []walkietalkie{
walkietalkie{ in: make(chan int, 6), out: results, quit: make(chan bool, 1) },
walkietalkie{ in: make(chan int, 6), out: results, quit: make(chan bool, 1) },
walkietalkie{ in: make(chan int, 6), out: results, quit: make(chan bool, 1) },
}
go work1(w[0])
go work2(w[1])
go work3(w[2])
// Iterate over work items
l := len(items)
for i, e := range items {
// Send the work item to each worker
for _, f := range w {
f.in <- e // send the work item
if i == l - 1 { // This is the last input, close the channel
close(f.in)
}
}
}
// Read all the results from the workers
for {
select {
case r, more := <-results:
if more {
fmt.Println(r)
} else {
continue
}
default:
break
}
}
}
You have a few problems.
For 1, reading from a channel with multiple return values like
case a, more := <-q.in
Will proceed on a closed channel, with more being set to false. In your case the default is never hit.
But those are in goroutines and wouldn't stop the program from exiting. The problem is your main goroutine is doing the same thing. Also, as it turns out, break will break out of selects as well as for loops. So if you want to break the for loop then you need to use a label and break LABEL.
As an alternative, you could also just return instead of breaking in your main goroutine.
package main
import (
"fmt"
"time"
)
func main() {
intChan := make(chan int, 1)
ticker := time.NewTicker(time.Second)
go func() {
for _ = range ticker.C {
select {
case intChan <- 1:
case intChan <- 2:
case intChan <- 3:
}
}
/*defer */
fmt.Println("End. [sender]")
}()
var sum int
for e := range intChan {
fmt.Printf("Received: %v\n", e)
sum += e
if sum > 10 {
fmt.Printf("Got: %v\n", sum)
break
//ticker.Stop()
}
}
fmt.Println("End. [receiver]")
//time.Sleep(10)
}
I'm new in golang. In this code, I want print "End. [sender]" once when the goroutine is over.
I try to use ticker.stop(), or even time.sleep(), defer , but no effect.
What's wrong with it, please give me some idea. thanks
As documentation says, ticker.Stop doesn't close channel. So you must not expect break-loop. You can add new channel for quit.
package main
import (
"fmt"
"time"
)
func main() {
intChan := make(chan int, 2)
ticker := time.NewTicker(time.Second)
quit := make(chan bool)
go func() {
loop:
for {
select {
case <-ticker.C:
select {
case intChan <- 1:
case intChan <- 2:
case intChan <- 3:
}
case <-quit:
break loop
}
}
/*defer */
fmt.Println("End. [sender]")
close(intChan)
}()
var sum int
for e := range intChan {
fmt.Printf("Received: %v\n", e)
sum += e
if sum > 10 {
fmt.Printf("Got: %v\n", sum)
quit <- true
//break
//ticker.Stop()
}
}
fmt.Println("End. [receiver]")
//time.Sleep(10)
}
http://play.golang.org/p/r92-KtQEGl
I am trying to execute this code. It throws a deadlock error.
What am I missing?
package main
import "tour/tree"
import "fmt"
// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, ch chan int){
var temp chan int
ch <- t.Value
if t.Left!=nil{go Walk(t.Left,temp)}
if t.Right!=nil{go Walk(t.Right,temp)}
for i := range temp{
ch <- i
}
close(ch)
}
// Same determines whether the trees
// t1 and t2 contain the same values.
func Same(t1, t2 *tree.Tree) bool
You need at least to initialize your channels (if the channel is nil, a range would block forever)
var temp chan int = make(chan int)
var ch chan int = make(chan int)
See http://play.golang.org/p/Gh8MZlyd3B (still deadlock but at least display results)
This version, using two temp channels, doesn't deadlock: http://play.golang.org/p/KsnmKTgZ83
package main
import "tour/tree"
import "fmt"
// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, ch chan int) {
var temp1 chan int = make(chan int)
var temp2 chan int = make(chan int)
ch <- t.Value
if t.Left != nil {
go Walk(t.Left, temp1)
}
if t.Right != nil {
go Walk(t.Right, temp2)
}
if t.Left != nil {
for i := range temp1 {
ch <- i
}
}
if t.Right != nil {
for i := range temp2 {
ch <- i
}
}
close(ch)
}
// Same determines whether the trees
// t1 and t2 contain the same values.
func Same(t1, t2 *tree.Tree) bool
func main() {
var ch chan int = make(chan int)
go Walk(tree.New(1), ch)
for i := range ch {
fmt.Println(i)
}
}
So I did this by by sending in a flag into the Walk function. This way it knows when it can close down the channel. I also think it's important to walk the tree in the right order Left node, Value, Right node.
package main
import (
"fmt"
"tour/tree"
)
// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, c chan int, d bool) {
if t.Left != nil {
Walk(t.Left, c, false)
}
c <- t.Value
if t.Right != nil {
Walk(t.Right, c, false)
}
if d {
close(c)
}
}
// Same determines whether the trees
// t1 and t2 contain the same values.
func Same(t1, t2 *tree.Tree) bool {
ch1 := make(chan int)
ch2 := make(chan int)
go Walk(t1, ch1, true)
go Walk(t2, ch2, true)
for {
v1, ok1 := <-ch1
v2, ok2 := <-ch2
if v1 != v2 {
return false
}
if ok1 != ok2 {
return false
}
if !ok1 && !ok2 {
return true
}
}
return false
}
func main() {
ch := make(chan int)
go Walk(tree.New(1), ch, true)
for i := range ch {
fmt.Println(i)
}
test1 := Same(tree.New(1), tree.New(1))
test2 := Same(tree.New(1), tree.New(2))
fmt.Println(test1, test2)
}