diff --git a/examples.txt b/examples.txt index bafeeba..1ad9588 100644 --- a/examples.txt +++ b/examples.txt @@ -27,15 +27,15 @@ Recursion Goroutines Channels Channel Buffering -# Channel Directions -# Synchronization +Channel Synchronization +Channel Directions Select Timeouts +Non-Blocking Channel Operations +# Closing Channels # Scatter Gather # Rate Limiting # Worker Pools -# Non-Blocking Channel Operations -# Closing Channels Timers Tickers # State Goroutine diff --git a/examples/channel-directions/channel-directions.go b/examples/channel-directions/channel-directions.go index d7092e2..ad59d0c 100644 --- a/examples/channel-directions/channel-directions.go +++ b/examples/channel-directions/channel-directions.go @@ -1,35 +1,30 @@ +// When using channels as function parameters, you can +// specify if a channel is meant to only send or receive +// values. This specificity further increases the +// type-safety of the program. + package main import "fmt" -func pinger(pings chan<- string) { - for i := 0; i <= 10; i++ { - pings <- "ping" - } +// This `ping` function only accepts a channel for sending +// values. It would be a compile-time error to try to +// receive on this channel. +func ping(pings chan<- string, msg string) { + pings <- msg } -func ponger(pings <-chan string, pongs chan<- string) { - for { - <-pings - pongs <- "pong" - } -} - -func printer(pongs <-chan string) { - for { - msg := <-pongs - fmt.Println(msg) - } +// The `pong` function accepts one channel for receives +// (`pings`) and a second for sends (`pongs`) +func pong(pings <-chan string, pongs chan<- string) { + msg := <-pings + pongs <- msg } func main() { - var pings chan string = make(chan string) - var pongs chan string = make(chan string) - - go pinger(pings) - go ponger(pings, pongs) - go printer(pongs) - - var input string - fmt.Scanln(&input) + pings := make(chan string, 1) + pongs := make(chan string, 1) + ping(pings, "passed message") + pong(pings, pongs) + fmt.Println(<-pongs) } diff --git a/examples/channel-directions/channel-directions.sh b/examples/channel-directions/channel-directions.sh new file mode 100644 index 0000000..8db5cb6 --- /dev/null +++ b/examples/channel-directions/channel-directions.sh @@ -0,0 +1,2 @@ +$ go run channel-directions.go +passed message diff --git a/examples/channel-synchronization/channel-synchronization.go b/examples/channel-synchronization/channel-synchronization.go new file mode 100644 index 0000000..0085edc --- /dev/null +++ b/examples/channel-synchronization/channel-synchronization.go @@ -0,0 +1,32 @@ +// We can use channels to synchronize execution +// across goroutines. Here's an example of using a +// blocking receive to wait for a goroutine to finish. + +package main + +import "fmt" +import "time" + +// This is the function we'll run in a goroutine. The +// `done` channel will be used to notify another +// goroutine that this function's work is done. +func worker(done chan bool) { + fmt.Print("working...") + time.Sleep(time.Second) + fmt.Println(" done") + + // Send a value to notify that we're done. + done <- true +} + +func main() { + + // Start a worker goroutine, giving it the channel to + // notify on. + done := make(chan bool, 1) + go worker(done) + + // Block until we receive a notification from the + // worker on the channel. + <-done +} diff --git a/examples/channel-synchronization/channel-synchronization.sh b/examples/channel-synchronization/channel-synchronization.sh new file mode 100644 index 0000000..b3496b3 --- /dev/null +++ b/examples/channel-synchronization/channel-synchronization.sh @@ -0,0 +1,6 @@ +$ go run channel-synchronization.go +working...done + +# If you removed the `<- done` line from this program, the +# program would exit before the `worker` even +# started. diff --git a/examples/non-blocking-channel-operations/non-blocking-channel-operations.go b/examples/non-blocking-channel-operations/non-blocking-channel-operations.go index ad13299..24cd6ff 100644 --- a/examples/non-blocking-channel-operations/non-blocking-channel-operations.go +++ b/examples/non-blocking-channel-operations/non-blocking-channel-operations.go @@ -1,3 +1,8 @@ +// Basic sends and receives on channels are blocking. +// However, we can use `select` with a `default` clause to +// implement _non-blocking_ sends, receives, and even +// non-blocking multi-way `select`s. + package main import "fmt" @@ -6,24 +11,30 @@ func main() { messages := make(chan string) signals := make(chan bool) - // Non-blocking receive. + // Here's a non-blocking receive. If a value is + // available on `messages` then `select` will take + // the `<-messages` `case` with that value. If not + // it will immediately take the `default` case. select { case msg := <-messages: fmt.Println("received message", msg) default: - fmt.Println("no messages received") + fmt.Println("no message received") } - // Non-blocking send. + // A non-blocking send works similarly. msg := "hi" select { case messages <- msg: fmt.Println("sent message", msg) default: - fmt.Println("no messages sent") + fmt.Println("no message sent") } - // Non-blocking multi-way select. + // We can use multiple `case`s above the `default` + // clause to implement a multi-way non-blocking + // select. Here we attempt non-blocking receives + // on both `messages` and `signals`. select { case msg := <-messages: fmt.Println("received message", msg) diff --git a/examples/non-blocking-channel-operations/non-blocking-channel-operations.sh b/examples/non-blocking-channel-operations/non-blocking-channel-operations.sh new file mode 100644 index 0000000..fe85dff --- /dev/null +++ b/examples/non-blocking-channel-operations/non-blocking-channel-operations.sh @@ -0,0 +1,4 @@ +$ go run non-blocking-channel-operations.go +no message received +no message sent +no activity diff --git a/examples/synchronization/synchronization.go b/examples/synchronization/synchronization.go deleted file mode 100644 index 9190053..0000000 --- a/examples/synchronization/synchronization.go +++ /dev/null @@ -1,30 +0,0 @@ -// We can use channels to synchronize execution -// accross goroutines. Here's an example of waiting -// for another goroutine to finish. - -package main - -import "fmt" -import "time" - -// The `done` channel will be used for -// synchronization. -func worker(done chan<- bool) { - fmt.Print("Working...") - time.Sleep(time.Second) - fmt.Println(" done") - - // Send a value to notify that the work is done. - done <- true -} - -func main() { - // Start a worker goroutine, give it the channel to - // notify on. - done := make(chan bool, 1) - go worker(done) - - // Block until we can receive a value from the worker - // over the channel. - <-done -} diff --git a/examples/synchronization/synchronization.sh b/examples/synchronization/synchronization.sh deleted file mode 100644 index 9a69de0..0000000 --- a/examples/synchronization/synchronization.sh +++ /dev/null @@ -1,6 +0,0 @@ -$ go run synchronization.go -Working... done - -# If you commented out the `<- done` line, the -# program would exit before the `worker` even -# started.