模拟写一个抓取的程序,发生deadlock,多次尝试之后,仍然无法解决,请高手帮忙看下?
1、定义了一个任务发布管道,brandChan,在最开始就把任务放进去
2、定义了两个协程,来处理这个抓取任务
3、抓取完成后结束,任务。
问题:
在程序跑了一段时间后,出现deadlock,但是管道是正常close了的,不太清楚具体是哪个地方发生了deadlock?
出错截图:
附详细代码:
package main
import (
"fmt"
"log"
"math/rand"
"sync"
"time"
)
func main() {
// 设置worker计数
wg := sync.WaitGroup{}
wg.Add(2)
//需要抓取的品牌
crawBrands := getCrawBrands()
if len(crawBrands) <= 0 {
panic("没有设置需要抓取的品牌!")
}
//定义抓取品牌chan
brandChan := make(chan BrandInfo, 5)
//定义抓取详情chan
//将任务放到chan
pushCrawBrand(crawBrands, brandChan)
//设置品牌抓取worker
for i := 0; i < 2; i++ {
go getCrawBrand(wg, brandChan)
}
wg.Wait()
log.Println("处理完成!")
}
// 从品牌chan获取任务
func getCrawBrand(wg sync.WaitGroup, brandChan chan BrandInfo) {
for {
brandInfo, ok := <-brandChan
if !ok {
break
}
// 1.请求商品列表第0页
// 2.存储商品列表数据到数据库
// 3.发布详情任务、销量任务
// 4.抓取下一页数据
isFirstPage := true
var crawPageResult *CrawPageResult
for {
if isFirstPage {
crawPageResult = getGoodsListWithPage(brandInfo, 0)
} else {
crawPageResult = getGoodsListWithPage(brandInfo, crawPageResult.NextPage)
}
//log.Printf("品牌 %v 抓取结果:%v \n", brandInfo, crawPageResult)
isFirstPage = false
// 抓取完成,跳出循环
if !crawPageResult.NeedNextPage {
log.Println("抓取完成,跳出循环。。。")
break
}
// 抓取失败,继续
if !crawPageResult.Success {
continue
}
// 抓取成功,后续处理
if crawPageResult.Success && len(crawPageResult.GoodsList) > 0 {
// todo 存储商品列表数据到数据库
//for _, goods := range crawPageResult.GoodsList {
//}
}
}
}
log.Println("减少计数器....................................")
wg.Done()
}
// 获取分页商品数据
func getGoodsListWithPage(brandInfo BrandInfo, page int) *CrawPageResult {
log.Printf("品牌 %v 开始抓取,第 %v 页...\n", brandInfo, page)
// 模拟抓取
time.Sleep(time.Second)
rand.Seed(time.Now().Unix()) // unix 时间戳,秒
// 设置抓取的商品列表
var goodsList []Goods
for i := 0; i < 20; i++ {
goodsList = append(goodsList, Goods{
SpuId: rand.Intn(9999999),
Title: fmt.Sprint(rand.Intn(9999999)),
SoldNum: rand.Intn(9999999),
Price: rand.Intn(9999999),
})
}
rand.Seed(time.Now().Unix()) // unix 时间戳,秒
// 抓取失败
randNum := rand.Intn(10)
if randNum%10 == 0 {
// 模拟抓取失败,例如网络请求错误
log.Printf("品牌 %v 抓取失败!\n", brandInfo)
return &CrawPageResult{Success: false, NeedNextPage: true, NextPage: page, GoodsList: goodsList}
}
// 抓取完成,不需要再抓取了
if randNum%5 == 0 {
// 模拟抓取完成
log.Printf("品牌 %v 抓取完成!\n", brandInfo)
return &CrawPageResult{Success: true, NeedNextPage: false, NextPage: 0, GoodsList: goodsList}
}
// 当前页抓取完成,需要继续抓取
nextPage := page + 1
return &CrawPageResult{Success: true, NeedNextPage: true, NextPage: nextPage, GoodsList: goodsList}
}
// 将抓取品牌的任务放到chan
func pushCrawBrand(crawBrands []BrandInfo, brandChan chan BrandInfo) {
for _, brandInfo := range crawBrands {
brandChan <- brandInfo
}
close(brandChan)
}
// 获取需要抓取的品牌
func getCrawBrands() []BrandInfo {
var brands []BrandInfo
brands = append(brands, BrandInfo{BrandId: 79, BrandName: "test1"})
brands = append(brands, BrandInfo{BrandId: 10106, BrandName: "test2"})
brands = append(brands, BrandInfo{BrandId: 10229, BrandName: "test3"})
brands = append(brands, BrandInfo{BrandId: 10010, BrandName: "test4"})
return brands
}
type Goods struct {
SpuId int //SPUID
Title string //标题
SoldNum int //销量
Price int //价格
}
// 分页抓取结果
type CrawPageResult struct {
Success bool //抓取是否成功
NeedNextPage bool //是否需要抓取下一页
NextPage int //下一页页数
GoodsList []Goods //抓取到的商品列表
}
// 品牌信息
type BrandInfo struct {
BrandId int //品牌ID
BrandName string // 品牌名称
}
WaitGroup要传地址,不能传值。改为: func getCrawBrand(wg *sync.WaitGroup, brandChan chan BrandInfo)