Concurrent Fan-Out Processing in Go
This challenge focuses on implementing a fan-out pattern using Go's concurrency features. Fan-out is a common architectural pattern where a single input is processed by multiple worker goroutines concurrently, and then the results are aggregated. This is useful for tasks that can be divided into independent sub-tasks and benefit from parallel execution, such as image processing, data analysis, or web scraping.
Problem Description
You are tasked with creating a Go program that implements a fan-out pattern to process a list of integers. The program should take a slice of integers as input, distribute these integers to a pool of worker goroutines, and then collect the results from each worker. Each worker should square its assigned integers and return the squared values. Finally, the main goroutine should aggregate the squared values into a single slice and return it.
Key Requirements:
- Concurrency: Utilize Go's goroutines and channels to achieve concurrent processing.
- Worker Pool: Implement a worker pool to manage the worker goroutines. The number of workers should be configurable.
- Input Distribution: Distribute the input integers evenly among the worker goroutines.
- Result Aggregation: Collect the results from all workers and combine them into a single slice.
- Error Handling: While this problem doesn't explicitly require error handling, consider how you might handle potential errors in a real-world scenario.
Expected Behavior:
The program should take a slice of integers and a number of workers as input. It should then concurrently square each integer using the worker pool and return a slice containing all the squared values in the order they were originally provided in the input slice.
Edge Cases to Consider:
- Empty Input Slice: Handle the case where the input slice is empty gracefully.
- Zero Workers: Handle the case where the number of workers is zero or negative.
- Large Input Slice: Consider the performance implications of a very large input slice and a large number of workers.
Examples
Example 1:
Input: []int{1, 2, 3, 4, 5}, 2
Output: []int{1, 4, 9, 16, 25}
Explanation: The input slice {1, 2, 3, 4, 5} is distributed among 2 workers. Worker 1 squares 1, 2, and 3, resulting in {1, 4, 9}. Worker 2 squares 4 and 5, resulting in {16, 25}. The results are then combined to produce {1, 4, 9, 16, 25}.
Example 2:
Input: []int{10, 20, 30}, 3
Output: []int{100, 400, 900}
Explanation: The input slice {10, 20, 30} is distributed among 3 workers. Each worker squares its assigned integer, resulting in {100, 400, 900}.
Example 3: (Edge Case)
Input: []int{}, 4
Output: []int{}
Explanation: The input slice is empty, so no processing is done, and an empty slice is returned.
Constraints
- The input slice can contain up to 1000 integers.
- The number of workers can be between 1 and 10 (inclusive).
- The integers in the input slice will be non-negative.
- The program should complete within 5 seconds for any valid input.
Notes
- Consider using channels to communicate between the main goroutine and the worker goroutines.
- Think about how to divide the work evenly among the workers. You might use a simple round-robin approach.
- The order of the results in the output slice must match the order of the input slice. This is a key requirement.
- While error handling isn't explicitly required, consider how you would handle errors in a production environment. For example, what if a worker panics?