go简单并发案例:并行计算与多任务下载

  • 时间:2025-11-19 19:42 作者: 来源: 阅读:0
  • 扫一扫,手机访问
摘要:一、案例 1:并行计算(计算多个数值的平方和)场景说明假设计算一组大整数的平方和,通过并发分配任务到多个 goroutine,提升计算效率(尤其数据量大时)。完整代码package main import ( "fmt" "sync" ) // 计算单个数字的平方,并将结果发送到通道 func square(num int, resultChan chan int, wg *sync.Wa

一、案例 1:并行计算(计算多个数值的平方和)

场景说明

假设计算一组大整数的平方和,通过并发分配任务到多个 goroutine,提升计算效率(尤其数据量大时)。

完整代码

package main

import (
	"fmt"
	"sync"
)

// 计算单个数字的平方,并将结果发送到通道
func square(num int, resultChan chan int, wg *sync.WaitGroup) {
	defer wg.Done() // 任务完成后通知WaitGroup
	resultChan <- num * num
}

func main() {
	numbers := []int{12345, 67890, 13579, 24680, 98765, 54321} // 待计算的数字
	resultChan := make(chan int, len(numbers)) // 带缓冲通道,存储计算结果
	var wg sync.WaitGroup

	// 1. 启动goroutine并行计算
	wg.Add(len(numbers)) // 等待的任务数=数字个数
	for _, num := range numbers {
		go square(num, resultChan, &wg)
	}

	// 2. 单独启动goroutine等待所有计算完成后关闭通道
	go func() {
		wg.Wait()
		close(resultChan) // 所有计算完成后关闭通道,避免range阻塞
	}()

	// 3. 从通道读取结果并累加总和
	total := 0
	for squareResult := range resultChan {
		total += squareResult
	}

	fmt.Printf("所有数字的平方和:%d
", total)
}

核心逻辑解析

  1. 任务拆分:将每个数字的平方计算作为独立任务,分配给不同 goroutine;
  2. 结果收集:用带缓冲通道 resultChan 接收每个任务的计算结果,避免阻塞;
  3. 同步控制:sync.WaitGroup 等待所有计算任务完成;单独的 goroutine 在所有任务完成后关闭通道,确保 for range 能正常退出;
  4. 效率提升:多个数字的平方计算并行执行,比串行计算节省时间(尤其数字量大或计算复杂时)。

输出结果

所有数字的平方和:18260239032

二、案例 2:多任务下载(并发下载多个文件)

场景说明

模拟从多个 URL 下载文件,通过并发下载提升整体效率(网络 IO 操作适合并发)。

完整代码

package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"sync"
	"time"
)

// 下载单个文件
func downloadFile(url string, saveDir string, wg *sync.WaitGroup, errChan chan error) {
	defer wg.Done()

	// 1. 创建保存目录(若不存在)
	if err := os.MkdirAll(saveDir, 0755); err != nil {
		errChan <- fmt.Errorf("创建目录失败:%w", err)
		return
	}

	// 2. 发送HTTP请求
	startTime := time.Now()
	resp, err := http.Get(url)
	if err != nil {
		errChan <- fmt.Errorf("请求%s失败:%w", url, err)
		return
	}
	defer resp.Body.Close()

	// 3. 检查响应状态
	if resp.StatusCode != http.StatusOK {
		errChan <- fmt.Errorf("下载%s失败,状态码:%d", url, resp.StatusCode)
		return
	}

	// 4. 创建本地文件
	filename := filepath.Base(url) // 从URL提取文件名
	savePath := filepath.Join(saveDir, filename)
	file, err := os.Create(savePath)
	if err != nil {
		errChan <- fmt.Errorf("创建文件%s失败:%w", savePath, err)
		return
	}
	defer file.Close()

	// 5. 写入文件内容
	_, err = io.Copy(file, resp.Body)
	if err != nil {
		errChan <- fmt.Errorf("写入文件%s失败:%w", savePath, err)
		return
	}

	// 6. 下载成功
	duration := time.Since(startTime)
	fmt.Printf("✅ 成功下载:%s → %s(耗时:%v)
", url, savePath, duration)
}

func main() {
	// 待下载的文件URL列表
	urls := []string{
		"https://golang.org/lib/godoc/images/footer-gopher.jpg",
		"https://www.runoob.com/wp-content/uploads/2019/09/go-logo.png",
		"https://www.baidu.com/img/flexible/logo/pc/result.png",
	}
	saveDir := "./downloads" // 保存目录
	var wg sync.WaitGroup
	errChan := make(chan error, len(urls)) // 收集错误信息

	// 1. 启动goroutine并发下载
	wg.Add(len(urls))
	for _, url := range urls {
		go downloadFile(url, saveDir, &wg, errChan)
	}

	// 2. 等待所有下载任务完成后关闭错误通道
	go func() {
		wg.Wait()
		close(errChan)
	}()

	// 3. 处理错误信息
	for err := range errChan {
		if err != nil {
			fmt.Printf("❌ 下载错误:%v
", err)
		}
	}

	fmt.Println("所有下载任务已完成")
}

三、两个案例的共性与差异

共性

  1. 任务拆分:将整体任务拆分为独立子任务,每个子任务由单独 goroutine 处理;
  2. 同步机制:均使用 sync.WaitGroup 等待所有子任务完成,避免主程序提前退出;
  3. 结果收集:通过通道(chan)收集子任务的结果或错误信息,实现 goroutine 间通信。

差异

维度

并行计算案例

多任务下载案例

任务类型

CPU 密集型(计算为主)

IO 密集型(网络请求为主)

性能瓶颈

CPU 处理能力

网络带宽 / 远程服务器响应速度

并发度提议

不宜超过 CPU 核心数(避免频繁切换)

可适当提高(IO 等待时不占用 CPU)

通道作用

传递计算结果

传递错误信息

四、实战注意事项

  1. 并发度控制:CPU 密集型任务:并发数 ≈ CPU 核心数(可通过 runtime.GOMAXPROCS 调整);IO 密集型任务:并发数可适当提高(如 10-100),但需避免过大导致网络拥堵;
  2. 资源限制:多任务下载时需思考目标服务器的并发限制,避免被封禁;
  3. 错误处理:必须处理子任务的错误(如通过错误通道收集),避免错误被忽略;
  4. 通道缓冲:根据任务数量设置合适的通道缓冲大小,减少阻塞;
  5. 资源释放:用 defer 确保文件、网络连接等资源正确释放,避免泄漏。

通过这两个案例可看出,Go 的并发模型(goroutine + channel + 同步工具)能简洁高效地实现并行任务处理,无论是计算密集型还是 IO 密集型场景都能很好适配。

  • 全部评论(0)
手机二维码手机访问领取大礼包
返回顶部