Trying to learn how channels work?

Hi,

This is my first post. Looking forward to grow in the community. :slight_smile:

I am currently learning Go and I’m at the channels topic. Please take a look at this code:

package main
import "fmt"

func WriteToChannel(s chan int, quit chan bool) {
	for i := 0; i < 10; i++ {
		s <- 0
	}

	quit <- true
}

func main() {
	var s chan int = make(chan int)
	var quit chan bool = make(chan bool)

	go WriteToChannel(s, quit)

	for {
		// s has no buffer. Therefore it becomes a synchronous operation that blocks all goroutines (except this main).
		// So it becomes a deadlock because it is waiting for a result coming from WriteToChannel that is blocked.
		fmt.Println(<- s)

		// quit has no buffer. Same reason as s.
		if <- quit {
			break
		}
	}
}

In this code, I’m trying to prove that if channel s and quit has no buffer, they block all operations (become synchronous operations), which both will never receive a value as they are waiting for values coming from WriteToChannel function that is blocked.

If I run the code, it gives a deadlock error. If I put buffer size on them, it worked.

However, I’m not sure with my conclusion. Am I making the right sense of this code? If you can also describe more of what I missed it would be also very helpful.

Cheers,

alectora

If you follow your code, you will notice that every time your for loop runs (the one inside main()) it needs to read one value from s, and then another value from quit, and then the entire loop repeats again.

I believe you are expecting if <- quit to render false if the channel is empty, but that isn’t how the channel works. It will wait until it gets a boolean value sent through the channel before it proceeds, so your code deadlocks the very first time it gets to if <- quit because it will wait until it has a value from quit, which won’t ever happen at this point because your WriteToChannel() function can no longer fill values into s because it has no buffer. As a result, both pieces of code get deadlocked inside of their respective for loops.

One way to fix this is to use a select statement, which is similar to a switch but is designed to work with channels. Each case in a select will not block when the channel has no values being sent through it, so it ends up being easier to read because you can write code very similar to what you have now. Here is an example of this: https://play.golang.org/p/MU5GGZnN_z

Now I did have to change one other thing with your code - the break statement would only break out of the select statement (and not the for loop). There are a few ways to approach this is a return statement won’t work for you. For example, you could use labels as described here - https://stackoverflow.com/questions/11104085/in-go-does-a-break-statement-break-from-a-switch-select Or you could have the for loop use a boolean variable that you set (eg for doWork { ... } and then set that to false when you get a message through the quit channel. You could also do what I did and return when you are ready to quit.

2 Likes

Yes I was expecting the for loop in main to loop the channel until they get the result. So that was wrong. :joy:
So this means I don’t need the for loop in main right? cause they wait for results anyway?

Just to confirm, so if I set the buffer on those values, s and quit channels will still wait for values, but the goroutines will still operate right (which is the reason why the WaitForChannel can finish its operation)?

Also, is it correct if I say Go channels feature is mostly similar like JavaScript’s promises pattern and Java Future library?

I’ll check on statement, it looks nice, but probably in different use case.

Cheers,

Alectora

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.