Mahesh S
Mahesh S

Reputation: 159

Rewrite channel while listening on it and for loop handling

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

Answers (2)

Volker
Volker

Reputation: 42413

  1. 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.

  2. There is no difference between variant A and B because you do not close the channel. But...

  3. 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

leaf bebop
leaf bebop

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

Related Questions