模拟写一个抓取的程序,发生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)

4年前 评论
讨论数量: 1

WaitGroup要传地址,不能传值。改为: func getCrawBrand(wg *sync.WaitGroup, brandChan chan BrandInfo)

4年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!