Go语言使用channel收发数据异常 批量爬取URL写入文件问题
问题根因
你的代码存在3个核心问题导致channel无法正常工作:
- 错误的channel关闭时机:你在每个
getpageCanal函数中都加了defer close(canal),第一次调用getpageCanal执行结束就会关闭channel,后续其他调用往已关闭的channel写入数据会直接触发panic,就算未触发panic,主函数的range循环也会因为channel关闭提前退出,无法接收后续数据。 - 无缓冲channel同步调用导致死锁:你当前是同步调用
getpageCanal,无缓冲channel的发送操作必须有接收方同时就绪才能执行成功,你的主函数接收逻辑在所有getpageCanal调用结束后才执行,第一个getpageCanal执行到写入channel的步骤就会永久阻塞,程序直接死锁。 - 潜在panic和资源泄漏风险:
http.Get返回的响应体page.Body没有主动关闭,会导致TCP连接泄漏;如果http.Get请求出错,page变量会是nil,后续调用page.Body会直接触发空指针panic。
修复方案
我们用sync.WaitGroup来统计所有请求协程的执行状态,等所有请求完成后再统一关闭channel,同时给每个请求开启独立goroutine实现并发,完善错误和资源处理逻辑,修复后代码如下:
package main import ( "fmt" "io/ioutil" "net/http" "os" "sync" ) func main() { initial := "https://www1.medion.de/downloads/index.pl?op=detail&id=" ending := "&type=treiber&lang=uk" links := []string{} os.Remove("dump.txt") dumpFile, err := os.Create("dump.txt") if err != nil { fmt.Println("创建文件失败:", err) return } defer dumpFile.Close() // 最后统一关闭文件 c := make(chan string) var wg sync.WaitGroup for i := 16000; i < 16004; i++ { links = append(links, initial+fmt.Sprint(i)+ending) } fmt.Println(links[0]) for _, link := range links { wg.Add(1) // 每个请求计数+1 // 开启goroutine并发请求 go func(target string) { defer wg.Done() // 请求完成计数-1 getpageCanal(c, target) }(link) } // 单独开一个goroutine等所有请求完成后关闭channel go func() { wg.Wait() close(c) }() // 遍历channel接收所有数据,channel关闭后自动退出循环 for el := range c { fmt.Println(el) n, err := dumpFile.WriteString(el) if err != nil { fmt.Println("写入文件失败:", err) return } if n == 0 { fmt.Println("无数据写入") } } fmt.Println("所有数据处理完成") } func getpageCanal(canal chan string, url string) { page, err := http.Get(url) if err != nil { fmt.Println("请求失败:", url, err) return } defer page.Body.Close() // 读完响应自动关闭Body释放资源 content, er2 := ioutil.ReadAll(page.Body) if er2 != nil { fmt.Println("读取响应失败:", url, er2) return } canal <- string(content) }
逻辑说明
sync.WaitGroup会跟踪所有请求协程的执行状态,所有请求完成后才会关闭channel,避免提前关闭的问题- 每个请求对应独立goroutine,无缓冲channel的发送操作和主函数的接收操作同时运行,不会出现阻塞死锁
- 所有资源(响应Body、文件)都加了defer自动释放,错误逻辑做了提前返回处理,避免空指针panic
内容的提问来源于stack exchange,提问作者dreamer
相关产品推荐
相关产品推荐

