Home

Using a buffered channel as a counter in Go

Sunday 09 September 2012

Go channels allow you to pass messages between goroutines in a threadsafe manner.
numbers := make(chan int)
go func(){
    for i := range numbers {
        fmt.Println(i)
    }
}()
go func(){
    for i := 0; i < 10; i++{
        numbers<-i
    }
    close(numbers)
}()
Here we make an unbuffered, synchronous channel. We send it 10 numbers in one goroutine, and read them from the channel and print them in another. Whenever we send a number into the channel we have to wait until the other goroutine reads from it before continuing. By the way, that range numbers reads from the channel until it is closed.
A buffered channel allows you to pass a certain number of values into a channel before it blocks. Once it blocks you must wait until at least one value has been read from it. make takes a second optional parameter which determines the capacity of the channel, or how buffered it is. For example, make(chan int, 10) would allow us to pass all 10 values from one goroutine into the channel without waiting for the other goroutine to read them.
But where are those values that are in the buffered channel? How can we access them? The answer is that we must read from the channel. There is no other way to change those values. And that suggests that we can use a buffered channel as a threadsafe store. We can put our value in by sending on the channel, and retrieve it by reading. If any other goroutine wants to read it, it will have to wait for us to put it back in the channel. We could use this to implement a threadsafe counter:
var elephants = make(chan int, 1) //buffered with space for 1 int
func init(){
    elephants<-0
}
func iSawAnElephant(){
    v := <- elephants //read current number
    elephants <- v+1 //write incremented number
}
Any number of goroutines can call iSawAnElephant() without the counter getting muddled. We need to make sure the channel has a value in there to start with, else the first call to iSawAnElephant() will block as it tries to read the current value.

Next (Logging Levels)