Reputation: 159
While trying few experiments in channels, I came up with below code:
var strChannel = make(chan string, 30)
var mutex = &sync.Mutex{}
func main() {
go sampleRoutine()
for i := 0; i < 10; i++ {
mutex.Lock()
strChannel <- strconv.FormatInt(int64(i), 10)
mutex.Unlock()
time.Sleep(1 * time.Second)
}
time.Sleep(10 * time.Second)
}
func sampleRoutine() {
/* A: for msg := range strChannel{*/
/* B: for {
msg := <-strChannel*/
log.Println("got message ", msg, strChannel)
if msg == "3" {
mutex.Lock()
strChannel = make(chan string, 20)
mutex.Unlock()
}
}
}
Basically here while listening to a given channel, I am assigning the channel variable to a new channel in a specific condition (here when msg == 3).
When I use the code in comment block B it works as expected, i.e. the loop moves on to the newly created channel and prints 4-10.
However comment block A which I believe is just a different way to write the loop doesn't work i.e. after printing "3" it stops.
Could someone please let me know the reason for this behavior?
And also is code like this where a routine listening on a channel, creating a new one safe?
Upvotes: 2
Views: 268
Reputation: 42413
Sending and reading from a channel do not need to be protected by a mutex: They act as synchronisation primitives by themself (that's one of the main ideas behind sending/receiving) on a channel.
There is no difference between variant A and B because you do not close the channel. But...
Resigning a new channel to strChannel
while iterating the old channel is wrong. Don't do that. Not even with the B variant. Think about range strChannel
as "please range over the values in the channel currently stored in the variable strChannel
". This range will "continue on the original channel" and storing a new channel in the same variable doesn't change this. Avoid such code!
Upvotes: 1
Reputation: 8222
In Go, for
statement evaluate the value on the right side of range
before the loop begins.
That means changing the value of the variable on the right side of range
will take no effects. So in your code, in Variant A, msg
is ever-iterating over the orignal channel and never changed. In Varaint B, it works as intended as the channel is being evaluated per iteration.
The concept is a little bit tricky. It does not mean you cannot modify items of the slice
or map
on the right side of range
. If you look deeper into it, you will find that in Go, map
and slice
stores a pointer, and modify its item does not change that pointer, so it has effects.
It is even more trickier in case of array
. Modifying item of an array
on the right side of range
has no effects. This is due to Go's mechanism about storing array as a value.
Playground examples: https://play.golang.org/p/wzPfGHFYrnv
Upvotes: 2