diff --git a/examples.txt b/examples.txt index 1ad9588..42be2ef 100644 --- a/examples.txt +++ b/examples.txt @@ -32,7 +32,8 @@ Channel Directions Select Timeouts Non-Blocking Channel Operations -# Closing Channels +Closing Channels +Range over Channels # Scatter Gather # Rate Limiting # Worker Pools diff --git a/examples/closing-channels/closing-channels.go b/examples/closing-channels/closing-channels.go index 1758887..575b4e9 100644 --- a/examples/closing-channels/closing-channels.go +++ b/examples/closing-channels/closing-channels.go @@ -1,33 +1,50 @@ +// _Closing_ a channel indicates that no more values +// will be sent on it. This can be useful to communicate +// completion to the channel's receivers. + package main import "fmt" -import "time" +// In this example we'll use a `jobs` channel to +// communicate work to be done from the `main()` goroutine +// to a worker goroutine. When we have no more jobs for +// the worker we'll `close` the `jobs` channel. func main() { - jobs := make(chan bool, 5) + jobs := make(chan int, 5) done := make(chan bool) + // Here's the worker goroutine. It repeatedly receives + // from `jobs` with `j, more := <-jobs`. In this + // special 2-value form of receive, the `more` value + // will be `false` if `jobs` has been `close`d and all + // values in the channel have already been received. + // We use this to notify on `done` when we've worked + // all our jobs. go func() { for { - _, more := <-jobs + j, more := <-jobs if more { - fmt.Println("received job") + fmt.Println("received job", j) } else { - fmt.Println("received all") + fmt.Println("received all jobs") done <- true return } } }() - for i := 0; i < 5; i++ { - jobs <- false - fmt.Println("sent job") + // This sends 3 jobs to the worker over the `jobs` + // channel, then closes it. + for j := 1; j <= 3; j++ { + jobs <- j + fmt.Println("sent job", j) } - - time.Sleep(100 * time.Millisecond) close(jobs) - fmt.Println("sent all") + fmt.Println("sent all jobs") + // We await the worker using the + // [synchronization](channel-synchronization) approach + // we saw earlier. <-done } diff --git a/examples/closing-channels/closing-channels.sh b/examples/closing-channels/closing-channels.sh new file mode 100644 index 0000000..013f8a8 --- /dev/null +++ b/examples/closing-channels/closing-channels.sh @@ -0,0 +1,12 @@ +$ go run closing-channels.go +sent job 1 +received job 1 +sent job 2 +received job 2 +sent job 3 +received job 3 +sent all jobs +received all jobs + +# The idea of closed channels leads naturally to our next +# example: `range` over channels. diff --git a/examples/range-over-channels/range-over-channels.go b/examples/range-over-channels/range-over-channels.go new file mode 100644 index 0000000..0c9437d --- /dev/null +++ b/examples/range-over-channels/range-over-channels.go @@ -0,0 +1,26 @@ +// In a [previous](range) example we saw how `for` and +// `range` provide iteration over basic data structures. +// We can also use this syntax to iterate over +// values received from a channel. + +package main + +import "fmt" + +func main() { + + // We'll iterate over 2 values in the `queue` channel. + queue := make(chan string, 2) + queue <- "one" + queue <- "two" + close(queue) + + // This `range` iterates over each element as it's + // received from `queue`. Because we `close`d the + // channel above, the iteration terminates after + // receiving the 2 elements. If we didn't `close` it + // we'd block on a 3rd receive in the loop. + for elem := range queue { + fmt.Println(elem) + } +} diff --git a/examples/range-over-channels/range-over-channels.sh b/examples/range-over-channels/range-over-channels.sh new file mode 100644 index 0000000..feea741 --- /dev/null +++ b/examples/range-over-channels/range-over-channels.sh @@ -0,0 +1,7 @@ +$ go run range-over-channels.go +one +two + +# This example also showed that it's possible to close +# a non-empty channel but still have the remaining +# values be received.