A Programmer's Guide To Go Part 3 - Goroutines & Concurrency
Written by Mike James   
Thursday, 13 February 2020
Article Index
A Programmer's Guide To Go Part 3 - Goroutines & Concurrency
Blocking Operations
Channels
Buffered Channels

Buffered Channels

Now we have to look at buffered channels. In the case of a buffered channel the blocking rules are that the routine that reads from the channel blocks until there is something to read. The routine that writes only blocks only if the channel is full and a value cannot be written.

Buffered channels basically allow situations where a goroutine can continue to fil a buffer when there is no other goroutine ready to empty it. It can also be used to implement classic concurrency object such as the semaphore. You can use the size of the buffer to limit the number of routines accessing a resource.

It is difficult to find a very simple example of using buffered channels but this one is as close to simple as I can manage. Consider the following main program:

func main() {
    runtime.GOMAXPROCS(1)
   ch := make(chan int, 2)
    go count(ch)
    for {
        j := <-ch
        if j < 0 {
            break
        }
        fmt.Println("main", j)
    }
}

This simply sets up a channel with two elements and then reads values from it printing them as it goes - again -1 is taken as a signal to stop.

The goroutine is just:

func count(ch chan int) {
    for i := 0; i < 10; i++ {
        ch <- i
        fmt.Println("goroutine", i)
    }
    ch <- -1
}

This adds values to the channel and then prints the value.

What you will see is something like:

goroutine 0
goroutine 1
goroutine 2
main 0
main 1
main 2
main 3
goroutine 3
goroutine 4

and so on.

If you have been following the explanations this might seem confusing. The really confusing one is the way main 3 is printed before goroutine 3. Does this mean the main routine read the value before the goroutine wrote it to the channel? No of course not.

What happens is that the main program first blocks when it tries to read from the channel and the goroutine gets to run and it stores the first value in the channel and prints the value. It then stores the second value in the channel and prints it but then blocks when it tries to store the third value as the channel is full. Notice it has printed two values.

The main program now gets to run and it reads a value from the channel but when it starts to print the value this is a system call so the scheduler is called and it transfers the thread to the goroutine which can now write another value to the channel because one has been removed. Notice that the goroutine has now printed three values and is now blocked again so the main function is restarted and prints its first value - and so on. The actual behavior is complicated but the pair  of goroutines fill and empty the channel as the thread is switched between the goroutines. 

In the case of main printing 3 before the goroutine prints 3 is explained by the scheduler allowing the main program to run just after the goroutine has stored 3 in the channel and before it has printed the value.

The order that things happen isn't deterministic even less so if multiple threads are involved but the order that the channel is filled and emptied is.


goicon1

More Information:

http://golang.org

Related articles:

Go Programming Language Turns 3       

Getting started with Google's Go

Why invent a new language? Go creator explains

Ready to Go - Go Reaches Version 1

Go in Google App Engine

Google App Engine Go-es Forward

Go with Google - Yet Another Language!

 

Banner

To be informed about new articles on I Programmer, sign up for our weekly newsletter, subscribe to the RSS feed and follow us on Twitter, Facebook or Linkedin.

 

espbook

 

Comments




or email your comment to: comments@i-programmer.info

<ASIN:0321817141>

<ASIN:1478355824>

time.Sleep(time.Second)


Last Updated ( Friday, 14 February 2020 )