Possible read collision on buffered channel?

I have a buffered channel that are read by multiple (4 in this example) go routines.
queue := make(chan string, 10000) // a large buffered channel, have frequent messages sent to the channel

Each go routine checks the number of elements available in the channel and process them all.
for i :=0; i< 4; i++{ // spun 4 go routines
go func() {
for {
for elem := range queue {
// do something with the elem from the channel
}
}
}
}

Will multiple go routines collide? In other words, could different go routine grab the same elem in the channel, or while one go routine is reading the buffer to len(queue), the other go routines already read and processed some of the elements? How to block other go routine from reading while one go routine is reading?

Thanks!

The way you operate on the channels appears not to be idiomatic. In the example you have given, the 4 goroutines will collide and read the same values multiple times.

I wrote a detailed example which seems to satisfy what you’re trying to do in my blog.

Hey @Gary,

If you are asking whether or not if you are listening on a channel in more than 1 goroutine, if it’s possible that there could be a data race and both goroutines read off a single value at the same time, the simple answer is no.

Channels are used for synchronization and sharing data this way and are completely goroutine safe :slight_smile:

@radovskyb Wouldn’t this require to fan out the queue to each of the goroutines? Like so:

for i := 0; i < 4; i++{ 
    go func(q chan string) { // instead of an empty parameter list
        // read and process elements from q
    }(q) // call the goroutine and pass a copy of the channel 
}

@Gary:

Each go routine checks the number of elements available in the channel and process them all.

I don’t think the range loop will work this way. As soon as one goroutine blocks while processing an element, another goroutine will continue and read the next element from the channel. If your goal is to pass a continuous list of elements to a single goroutine, I guess you would need a different approach. E.g. have the producer of the elements fill four separate channels and have each goroutine read from their own channel.

Hey @christophberger,

I was only just addressing this part that he asked because of how he titled the question

Basically I think maybe he was asking if there could possibly be data races with channels through goroutines where 2 goroutines take the same value off of a channel at the same time without using a fan out, but I could have misunderstood what he was asking :stuck_out_tongue:

But if he was asking how to actually send to multiple goroutines then I would use a fan out…

Maybe I should have read the question a little better if I completely mis represented what he was asking lol

I think this is exactly the question.

And I think you are right: Channels should handle reads from multiple goroutines without collisions, there should be no need for a fan-out via go func parameter.

(I peeked into chan.go and found that the hchan struct that represents a channel has a mutex and chanrecv(), that implements the receive operation, uses this mutex, and I also wrote a quick test that tries to force a collision but did not find any.)

2 Likes

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