使用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() <pre class='brush:php;toolbar:false;'>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() <p>req, _ := http.NewRequestWithContext(ctx, "GET", url, nil) resp, err := http.DefaultClient.Do(req)</p>
基本上就这些。并发下载的核心是合理调度goroutine,控制资源使用,加上必要的错误和超时处理,就能稳定高效地运行。