关于 Goroutines 和并发控制的 Golang 难题
下面是一道关于 Goroutines 和并发控制的 Golang 难题,它涉及到 Go 的并发编程模型、Goroutines、通道(Channels)以及 sync.WaitGroup 的使用:
问题描述:
你有一个需要并发执行的任务,其中有 100 个 URL 需要下载并处理。每个 URL 的下载和处理都是一个耗时的操作,且最多只能同时运行 5 个 Goroutine。每个 Goroutine 在处理完成后,应将结果(假设是下载的数据)传回主 Goroutine 进行汇总。
你需要:
- 实现一个并发下载的函数 downloadAndProcess,并确保在任何时刻都只有 5 个 Goroutine 同时运行。
- 所有下载任务完成后,主 Goroutine 要能够获取到每个 URL 对应的下载结果,并输出汇总后的总结果(例如所有 URL 的数据大小总和)。
要求:
- 不能使用第三方库。
- 需要用 sync.WaitGroup 和通道(Channel)来管理并发和数据传递。
提示:
- 你可以使用 sync.WaitGroup 来等待所有 Goroutine 完成。
- 使用一个带缓冲区的通道来限制并发的 Goroutine 数量(最多 5 个)。
- 使用一个无缓冲的通道将下载的结果传递给主 Goroutine。
示例代码框架:
package main
import (
"fmt"
"sync"
)
// downloadAndProcess 模拟下载并处理一个 URL 的函数
func downloadAndProcess(url string) int {
// 假设这里是耗时操作,返回下载内容的长度
// 实际应用中可以是 http.Get(url) 的结果长度
return len(url)
}
func main() {
urls := []string{
// 100 个不同的 URL 列表
"http://example.com/1",
"http://example.com/2",
// 省略其余 URL ...
}
var wg sync.WaitGroup
// 结果通道,用于收集每个 URL 的下载结果
results := make(chan int)
// 并发限制,最多允许 5 个 Goroutine 并发运行
concurrencyLimit := make(chan struct{}, 5)
// 完成下载后,主 Goroutine 获取结果并汇总
go func() {
var totalSize int
for size := range results {
totalSize += size
}
fmt.Println("所有 URL 下载完成,总大小:", totalSize)
}()
// 启动 Goroutines 来处理每个 URL
for _, url := range urls {
wg.Add(1)
go func(url string) {
defer wg.Done()
// 获取并发限制通道的许可
concurrencyLimit <- struct{}{}
// 模拟下载并处理 URL
size := downloadAndProcess(url)
// 将结果发送到通道
results <- size
// 释放并发限制通道的许可
<-concurrencyLimit
}(url)
}
// 等待所有 Goroutine 完成
wg.Wait()
// 所有 Goroutine 完成后,关闭结果通道
close(results)
}
难点分析:
- Goroutine 限制:最多只能有 5 个 Goroutine 并发运行,需要利用带缓冲区的通道来实现。
- 结果汇总:需要主 Goroutine 不断从通道中读取结果并累加,直到所有 Goroutine 都完成。
- 同步控制:使用 sync.WaitGroup 确保主 Goroutine 在所有子 Goroutine 完成后关闭通道。
这道题考察了并发控制、通道的使用、Goroutine 限制和同步操作等关键并发编程知识。