In this example we are going to process multiple images concurrently with goroutines. The process involves uploading, cropping and saving in order. We are using channels to tell next step to start. Once all steps are completed for the image processing, we close all its channels. Once all images are processed, the application exists. Since this is a concurrent operation, you would never guarantee which image would be processed first. By the way, we won't actually upload, crop and save images!


Example


main.go


package main

import (
"fmt"
"math/rand"
"sync"
"time"

"internal/image"
)

var (
wg sync.WaitGroup
files = []string{"image1.jpg", "image2.jpg", "image3.jpg", "image4.jpg"}
)

func main() {
rand.Seed(time.Now().UnixNano())

fmt.Println("START")

wg.Add(len(files))

for _, file := range files {
// Start uploading asynchronously.
go image.New(&wg, file).Upload()
}

wg.Wait()

fmt.Println("FINISH")
}

upload.go


We are randomly sleeping to simulate some processing time.


package image

import (
"fmt"
"math/rand"
"sync"
"time"
)

type Image struct {
wg *sync.WaitGroup
name string
}

func New(wg *sync.WaitGroup, name string) Image {
return Image{
wg: wg,
name: name,
}
}

func (i Image) Upload() {
defer i.wg.Done()

fmt.Println("upload(str) -", i.name)

wait := rand.Intn(len([]int{0, 1, 2, 3, 4, 5, 6}))
time.Sleep(time.Duration(wait) * time.Second)

fmt.Println("upload(end) -", i.name, wait, "sec")

cropDone := make(chan bool)
saveDone := make(chan bool)

// Start cropping asynchronously.
go i.crop(cropDone, saveDone)

// Upload ending depends on completion of crop and save operations so blocking here.
<- cropDone
<- saveDone
}

func (i Image) crop(cropDone, saveDone chan <- bool) {
fmt.Println("crop(str) -", i.name)

wait := rand.Intn(len([]int{0, 1, 2, 3, 4, 5, 6}))
time.Sleep(time.Duration(wait) * time.Second)

fmt.Println("crop(end) -", i.name, wait, "sec")

// Instruct channel that crop is done.
cropDone <- true // close(cropDone)

// Start saving asynchronously.
go i.save(saveDone)
}

func (i Image) save(saveDone chan <- bool) {
fmt.Println("save(str) -", i.name)

wait := rand.Intn(len([]int{0, 1, 2, 3, 4, 5, 6}))
time.Sleep(time.Duration(wait) * time.Second)

fmt.Println("save(end) -", i.name, wait, "sec")

// Instruct channel that save is done.
saveDone <- true // close(saveDone)
}

Test


As you can see below, the winner is image3!


START
upload(str) - image1.jpg
upload(str) - image2.jpg
upload(str) - image3.jpg
upload(str) - image4.jpg
upload(end) - image1.jpg 2 sec
crop(str) - image1.jpg
crop(end) - image1.jpg 0 sec
save(str) - image1.jpg
upload(end) - image4.jpg 3 sec
upload(end) - image3.jpg 3 sec
crop(str) - image3.jpg
crop(str) - image4.jpg
upload(end) - image2.jpg 5 sec
crop(str) - image2.jpg
crop(end) - image2.jpg 2 sec
crop(end) - image3.jpg 4 sec
save(str) - image2.jpg
save(str) - image3.jpg
save(end) - image3.jpg 0 sec
save(end) - image1.jpg 6 sec
crop(end) - image4.jpg 5 sec
save(str) - image4.jpg
save(end) - image2.jpg 3 sec
save(end) - image4.jpg 6 sec
FINISH

Total time
image3 - 7 sec
image1 - 8 sec
image2 - 10 sec
image4 - 14 sec