Solution 5: Go Concurrency
Let’s solve the challenge set in the previous lesson.
We'll cover the following...
Solution
To modify wPools.go so that each worker implements the functionality of wc(1), we can replace the calculation of the square with a call to the wc command-line tool using Go’s built-in os/exec package. ...
package main
import (
"fmt"
"os"
"os/exec"
"runtime"
"strconv"
"strings"
"sync"
"time"
)
type Client struct {
id int
text string
}
type Result struct {
job Client
lines int
words int
characters int
}
var size = runtime.GOMAXPROCS(0)
var clients = make(chan Client, size)
var data = make(chan Result, size)
func worker(wg *sync.WaitGroup) {
for c := range clients {
cmd := exec.Command("wc", "-l", "-w", "-c")
cmd.Stdin = strings.NewReader(c.text)
out, err := cmd.Output()
if err != nil {
fmt.Println(err)
continue
}
output := strings.Fields(string(out))
lines, _ := strconv.Atoi(output[0])
lines++
words, _ := strconv.Atoi(output[1])
characters, _ := strconv.Atoi(output[2])
res := Result{c, lines, words, characters}
data <- res
time.Sleep(time.Second)
}
wg.Done()
}
func create(n int) {
for i := 0; i < n; i++ {
c := Client{i, "Hello World!"}
clients <- c
}
close(clients)
}
func main() {
if len(os.Args) != 3 {
fmt.Println("Need #jobs and #workers!")
return
}
nJobs, err := strconv.Atoi(os.Args[1])
if err != nil {
fmt.Println(err)
return
}
nWorkers, err := strconv.Atoi(os.Args[2])
if err != nil {
fmt.Println(err)
return
}
go create(nJobs)
finished := make(chan interface{})
go func() {
for d := range data {
fmt.Printf("Client ID: %d\tlines: %d\twords: %d\tcharacters: %d\n", d.job.id, d.lines, d.words, d.characters)
}
finished <- true
}()
var wg sync.WaitGroup
for i := 0; i < nWorkers; i++ {
wg.Add(1)
go worker(&wg)
}
wg.Wait()
close(data)
fmt.Printf("Finished: %v\n", <-finished)
}
Code explanation
Lines 14–17: We replace the
integerfield in theClientstruct with atextfield that contains a sample text string.Lines 19–24: We also add three fields to the
Resultstruct to hold the number of lines, words, and characters in the text.Lines 30–49: In the
workerfunction, we replace the calculation of the square with a call to thewccommand-line tool. We create a newexec.Cmdstruct to represent the command, set itsStdinfield to a newstrings.Readerthat contains the text from theClient, and then call itsOutputmethod to run the command and capture its output. We then parse the output to extract the line count, word count, and character count and create a newResultstruct to send to thedatachannel.Line 53: Finally, in the
createfunction, we replace the integer value in theClientstruct with a sample"Hello World!"text.