Swiftorial Logo
Home
Swift Lessons
Matchups
CodeSnaps
Tutorials
Career
Resources

Concurrency Patterns: Fan-in and Fan-out in Go

Introduction

Concurrency patterns are essential in modern programming to efficiently manage parallel tasks. In Go, two important concurrency patterns are Fan-in and Fan-out. These patterns help manage and coordinate multiple goroutines effectively.

Fan-out

Fan-out refers to the process of starting multiple concurrent tasks from a single point. In Go, this often means spawning multiple goroutines to perform tasks concurrently.

Example:

func main() {
    jobs := make(chan int, 10)
    results := make(chan int, 10)

    for w := 1; w <= 3; w++ {
        go worker(w, jobs, results)
    }

    for j := 1; j <= 9; j++ {
        jobs <- j
    }
    close(jobs)

    for a := 1; a <= 9; a++ {
        <-results
    }
}

func worker(id int, jobs <-chan int, results chan<- int) {
    for j := range jobs {
        fmt.Println("worker", id, "started  job", j)
        time.Sleep(time.Second)
        fmt.Println("worker", id, "finished job", j)
        results <- j * 2
    }
}
                    

In the example above, we have a function worker that takes jobs from the jobs channel. We spawn three worker goroutines to process jobs concurrently, demonstrating the Fan-out pattern.

Fan-in

Fan-in is the opposite of Fan-out. It involves collecting results from multiple concurrent tasks into a single point. In Go, this typically means gathering output from multiple goroutines into a single channel.

Example:

func main() {
    c1 := make(chan string)
    c2 := make(chan string)

    go func() {
        time.Sleep(time.Second * 1)
        c1 <- "one"
    }()
    go func() {
        time.Sleep(time.Second * 2)
        c2 <- "two"
    }()

    for i := 0; i < 2; i++ {
        select {
        case msg1 := <-c1:
            fmt.Println("Received", msg1)
        case msg2 := <-c2:
            fmt.Println("Received", msg2)
        }
    }
}
                    

In this example, we have two goroutines that send messages to channels c1 and c2. The select statement is used to receive messages from both channels, demonstrating the Fan-in pattern.

Combining Fan-in and Fan-out

Often, you will need to combine both Fan-in and Fan-out patterns to efficiently manage concurrent tasks. This involves spawning multiple goroutines (Fan-out) and then collecting their results (Fan-in).

Example:

func main() {
    jobs := make(chan int, 100)
    results := make(chan int, 100)

    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(<-results)
    }
}

func worker(id int, jobs <-chan int, results chan<- int) {
    for j := range jobs {
        fmt.Println("worker", id, "started  job", j)
        time.Sleep(time.Second)
        fmt.Println("worker", id, "finished job", j)
        results <- j * 2
    }
}
                    

Here, jobs are distributed among three workers (Fan-out), and their results are collected in the results channel (Fan-in). This pattern is useful for parallel processing tasks and aggregating their results efficiently.

Conclusion

Understanding and implementing Fan-in and Fan-out patterns in Go can significantly enhance the efficiency and scalability of your programs. These patterns allow you to manage multiple concurrent tasks and aggregate their results seamlessly.