Corporate Training
Request Demo
Click me
Menu
Let's Talk
Request Demo

Tutorials

 Advanced Concurrency Patterns

30. Advanced Concurrency Patterns

Advanced concurrency patterns in Go involve techniques that enable efficient coordination and synchronization of concurrent tasks. These patterns can help you design high-performance, concurrent applications. In this tutorial, we'll explore some advanced concurrency patterns in Go with examples.

1. Worker Pool:

A worker pool is a common pattern for parallelizing tasks. It involves a fixed number of worker goroutines that pick up and process jobs from a queue.

package main

import (
    "fmt"
    "sync"
)

func worker(id int, jobs <-chan int, results chan<- int, wg *sync.WaitGroup) {
    defer wg.Done()
    for job := range jobs {
        result := job * 2
        results <- result
    }
}

func main() {
    const numJobs = 10
    const numWorkers = 3

    jobs := make(chan int, numJobs)
    results := make(chan int, numJobs)
    var wg sync.WaitGroup

    for i := 1; i <= numWorkers; i++ {
        wg.Add(1)
        go worker(i, jobs, results, &wg)
    }

    for i := 1; i <= numJobs; i++ {
        jobs <- i
    }
    close(jobs)

    go func() {
        wg.Wait()
        close(results)
    }()

    for result := range results {
        fmt.Println(result)
    }
}

 

2. Pipeline:

The pipeline pattern is useful for processing data in stages, where each stage is performed by a goroutine.

package main

import (
    "fmt"
)

func main() {
    numbers := []int{1, 2, 3, 4, 5}
    input := make(chan int)
    output := make(chan int)

    go func() {
        for _, n := range numbers {
            input <- n
        }
        close(input)
    }()

    go func() {
        for n := range input {
            output <- n * 2
        }
        close(output)
    }()

    for result := range output {
        fmt.Println(result)
    }
}

 

3. Pub-Sub (Publish-Subscribe):

The pub-sub pattern involves multiple subscribers receiving events or data published by a publisher. Channels can be used to implement this pattern.

package main

import (
    "fmt"
    "sync"
)

type Event struct {
    Data string
}

func main() {
    var wg sync.WaitGroup
    ch := make(chan Event, 10)

    for i := 0; i < 3; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            for event := range ch {
                fmt.Printf("Subscriber %d received event: %s\n", id, event.Data)
            }
        }(i)
    }

    publishers := 2
    for i := 0; i < publishers; i++ {
        go func(id int) {
            for j := 0; j < 5; j++ {
                event := Event{Data: fmt.Sprintf("Event %d from publisher %d", j, id)}
                ch <- event
            }
        }(i)
    }

    wg.Wait()
    close(ch)
}

 

4. Context and Cancellation:

The context package in Go allows you to gracefully cancel and manage goroutines. This is useful for terminating tasks when they are no longer needed.

package main

import (
    "context"
    "fmt"
    "time"
)

func worker(ctx context.Context) {
    for {
        select {
        case <-ctx.Done():
            fmt.Println("Worker is shutting down.")
            return
        default:
            fmt.Println("Worker is processing.")
            time.Sleep(1 * time.Second)
        }
    }
}

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    go worker(ctx)

    select {
    case <-ctx.Done():
        fmt.Println("Main is done.")
    }
}

 

These advanced concurrency patterns in Go offer powerful ways to handle concurrent tasks efficiently and effectively. Depending on your application's requirements, you can choose and adapt these patterns to build robust and high-performance concurrent applications.