0
0
Goprogramming~10 mins

Common concurrency patterns in Go

Choose your learning style9 modes available
Introduction

Concurrency lets your program do many things at the same time. Common patterns help you organize this work safely and clearly.

When you want to download many files at once without waiting for each to finish.
When you need to handle multiple user requests in a web server simultaneously.
When you want to process data in stages, like reading, transforming, and saving, all at the same time.
When you want to share work between several workers to finish faster.
When you want to safely share data between parts of your program running at the same time.
Syntax
Go
package main

import (
	"fmt"
	"sync"
)

func main() {
	// Example of a concurrency pattern
}

Go uses goroutines to run functions concurrently.

Channels help goroutines communicate safely.

Examples
This starts a new goroutine that runs the function concurrently.
Go
go func() {
	fmt.Println("Hello from a goroutine")
}()
This shows how to send and receive data between goroutines using a channel.
Go
ch := make(chan int)
go func() {
	ch <- 42 // send value to channel
}()
val := <-ch // receive value from channel
fmt.Println(val)
WaitGroup waits for goroutines to finish before main exits.
Go
var wg sync.WaitGroup
wg.Add(1)
go func() {
	defer wg.Done()
	fmt.Println("Work done")
}()
wg.Wait()
This is a worker pool pattern where multiple workers process jobs concurrently.
Go
jobs := make(chan int, 3)
results := make(chan int, 3)

// Worker function
worker := func(id int, jobs <-chan int, results chan<- int) {
	for j := range jobs {
		fmt.Printf("worker %d started job %d\n", id, j)
		results <- j * 2
	}
}

func main() {
	for w := 1; w <= 3; w++ {
		go worker(w, jobs, results)
	}
	for j := 1; j <= 5; j++ {
		jobs <- j
	}
	close(jobs)
	for a := 1; a <= 5; a++ {
		fmt.Println("result:", <-results)
	}
}
Sample Program

This program uses a worker pool pattern. It starts 3 workers that get jobs from a channel. Each worker does some work (simulated by sleep) and sends results back. The main function waits for all workers to finish and then prints the results.

Go
package main

import (
	"fmt"
	"sync"
	"time"
)

func worker(id int, jobs <-chan int, results chan<- int, wg *sync.WaitGroup) {
	defer wg.Done()
	for j := range jobs {
		fmt.Printf("Worker %d started job %d\n", id, j)
		time.Sleep(time.Second) // simulate work
		fmt.Printf("Worker %d finished job %d\n", id, j)
		results <- j * 2
	}
}

func main() {
	jobs := make(chan int, 5)
	results := make(chan int, 5)
	var wg sync.WaitGroup

	// Start 3 workers
	for w := 1; w <= 3; w++ {
		wg.Add(1)
		go worker(w, jobs, results, &wg)
	}

	// Send 5 jobs
	for j := 1; j <= 5; j++ {
		jobs <- j
	}
	close(jobs)

	// Wait for all workers to finish
	wg.Wait()
	close(results)

	// Collect results
	for res := range results {
		fmt.Println("Result:", res)
	}
}
OutputSuccess
Important Notes

Always close channels when no more values will be sent to avoid deadlocks.

Use WaitGroup to wait for goroutines to finish before exiting main.

Be careful to avoid race conditions when sharing data between goroutines.

Summary

Concurrency lets your program do many things at once using goroutines.

Channels help goroutines talk to each other safely.

Common patterns like worker pools and WaitGroups help organize concurrent work clearly.