使用goroutine与channel实现Go语言并发文件下载,通过WaitGroup等待任务完成,并用带缓冲channel控制最大并发数防止资源耗尽。

在Go语言中实现并发文件下载,核心思路是利用goroutine和channel来并行发起多个HTTP请求,同时控制并发数量防止资源耗尽。通过合理分配任务、合并结果和错误处理,可以高效地完成多个文件的下载。
使用Goroutine与WaitGroup控制并发
每个文件下载任务可以在独立的goroutine中执行,使用sync.WaitGroup等待所有任务完成。这种方式简单直接,适合数量不多的并发下载。
示例代码结构:
func downloadFile(url, filename string) error {
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
file, err := os.Create(filename)
if err != nil {
return err
}
defer file.Close()
io.Copy(file, resp.Body)
return nil}
立即学习“go语言免费学习笔记(深入)”;
func main() {
urls := []string{"https://www.php.cn/link/18c578f830e2897ac30e2c72e6e122a1", "https://www.php.cn/link/c2e4a009b0acaea484f4384134824c69"}
var wg sync.WaitGroup
for i, url := range urls {
wg.Add(1)
go func(u string, idx int) {
defer wg.Done()
err := downloadFile(u, fmt.Sprintf("file_%d", idx))
if err != nil {
log.Printf("下载失败: %s, 错误: %v", u, err)
}
}(url, i)
}
wg.Wait()}
立即学习“go语言免费学习笔记(深入)”;
限制最大并发数避免系统过载
当下载任务较多时,无限制地启动goroutine可能导致内存或连接数爆炸。可以通过带缓冲的channel作为信号量来控制最大并发数。
常见做法:
- 创建容量为N的channel,表示最多允许N个并发任务
- 每个goroutine开始前从channel接收信号,结束后释放
- 主协程循环发送任务,通过channel实现调度
semaphore := make(chan struct{}, 10) // 最多10个并发
for i, url := range urls {
go func(u string, idx int) {
semaphore <- struct{}{} // 获取令牌
err := downloadFile(u, fmt.Sprintf("file_%d", idx))
if err != nil {
log.Println("下载出错:", err)
}
<-semaphore // 释放令牌
}(url, i)
}
// 等待所有任务完成(可通过额外channel或time.Sleep简化)
time.Sleep(2 * time.Second)
结合Context实现超时与取消
真实场景中需要对下载任务设置超时机制,避免长时间阻塞。使用context.WithTimeout可统一管理生命周期。
关键点:
- 为每个请求绑定context,传递超时控制
- 在http.Get中使用带context的client
- 主逻辑可通过context提前取消所有任务
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel()req, _ := http.NewRequestWithContext(ctx, "GET", url, nil) resp, err := http.DefaultClient.Do(req)
基本上就这些。并发下载的核心是合理调度goroutine,控制资源使用,加上必要的错误和超时处理,就能稳定高效地运行。










