Chapter 6 Concurrency
6.5 channels
6.5.2 Buffered channels
concept
A buffered channel is a channel that can store one or more values before being received. This
type of channel does not mandate that both sending and receiving must be done between goroutines. The conditions under which a channel can block send and receive actions are also different. The receive action will only block if there are no values to receive in the channel. The send action will only block if the channel has no buffers available to hold the value being sent . This leads to a big difference between buffered and unbuffered channels: unbuffered channels guarantee that the sending and receiving goroutines will exchange data at the same time; buffered channels have no such guarantee .
Code combat
Let's look at an example using a buffered channel that manages a set of goroutines to receive and complete work.
Buffered channels provide a clear and intuitive way to do this, the code is as follows
// This sample program demonstrates how to use a buffered
// channel to work on multiple tasks with a predefined number
// of goroutines.
package main
import (
"fmt"
"math/rand"
"sync"
"time"
)
const (
numberGoroutines = 4 // Number of goroutines to use.
taskLoad = 10 // Amount of work to process.
)
// wg is used to wait for the program to finish.
var wg sync.WaitGroup
// init is called to initialize the package by the
// Go runtime prior to any other code being executed.
func init() {
// Seed the random number generator.
rand.Seed(time.Now().Unix())
}
// main is the entry point for all Go programs.
func main() {
// Create a buffered channel to manage the task load.
tasks := make(chan string, taskLoad)
// Launch goroutines to handle the work.
wg.Add(numberGoroutines)
for gr := 1; gr <= numberGoroutines; gr++ {
go worker(tasks, gr)
}
// Add a bunch of work to get done.
for post := 1; post <= taskLoad; post++ {
tasks <- fmt.Sprintf("Task : %d", post)
}
// Close the channel so the goroutines will quit
// when all the work is done.
close(tasks)
// Wait for all the work to get done.
wg.Wait()
}
// worker is launched as a goroutine to process work from
// the buffered channel.
func worker(tasks chan string, worker int) {
// Report that we just returned.
defer wg.Done()
for {
// Wait for work to be assigned.
task, ok := <-tasks
if !ok {
// This means the channel is empty and closed.
fmt.Printf("Worker: %d : Shutting Down\n", worker)
return
}
// Display we are starting the work.
fmt.Printf("Worker: %d : Started %s\n", worker, task)
// Randomly wait to simulate work time.
sleep := rand.Int63n(100)
time.Sleep(time.Duration(sleep) * time.Millisecond)
// Display we finished the work.
fmt.Printf("Worker: %d : Completed %s\n", worker, task)
}
}
operation result
go run listing24.go
Worker: 1 : Started Task : 1
Worker: 3 : Started Task : 3
Worker: 4 : Started Task : 4
Worker: 2 : Started Task : 2
Worker: 3 : Completed Task : 3
Worker: 3 : Started Task : 5
Worker: 4 : Completed Task : 4
Worker: 4 : Started Task : 6
Worker: 1 : Completed Task : 1
Worker: 1 : Started Task : 7
Worker: 3 : Completed Task : 5
Worker: 3 : Started Task : 8
Worker: 1 : Completed Task : 7
Worker: 1 : Started Task : 9
Worker: 2 : Completed Task : 2
Worker: 2 : Started Task : 10
Worker: 4 : Completed Task : 6
Worker: 4 : Shutting Down
Worker: 3 : Completed Task : 8
Worker: 3 : Shutting Down
Worker: 2 : Completed Task : 10
Worker: 2 : Shutting Down
Worker: 1 : Completed Task : 9
Worker: 1 : Shutting Down
The Chinese comments and explanations of the specific codes are as follows