新手问题 有一段代码实在看不懂,也问了其他人,求前辈帮忙

StubbornGrass · 2018年04月06日 · 最后由 satng 回复于 2018年04月25日 · 457 次阅读

看的一个网络爬虫课程有一段代码实在没看懂

这是 main 入口函数

package main

import (
    "crawler/engine"
    "crawler/zhenai/parser"

    "crawler/scheduler"
)

func main() {


    e := engine.ConcurrentEngine{
        Scheduler:&scheduler.SimpleScheduler{}, // 这里为什么要用取地址?
        WorkerCount:100,
    }

    e.Run(engine.Request{
        Url:        "http://www.zhenai.com/zhenghun",
        ParserFunc: parser.ParseCityList,
    })

}
package engine

import (
    "log"
)

type ConcurrentEngine struct {
    Scheduler   Scheduler
    WorkerCount int
}

type Scheduler interface {
    Submit(Request)
    ConfigurMasterWorkerChan(chan Request)
}

func (e *ConcurrentEngine) Run(seeds ...Request) {

    in := make(chan Request)
    out := make(chan ParseResult)

    e.Scheduler.ConfigurMasterWorkerChan(in) //  这一句话看了很久,一直不懂是什么意思

    for i := 0; i < e.WorkerCount; i++ {
        createWorker(in, out)
    }

    for _, r := range seeds {
        e.Scheduler.Submit(r)
    }


    itemCount := 0
    for {
        result := <-out
        for _, item := range result.Items {
            log.Printf("Got Item #%d:%v", itemCount,item)
            itemCount++
        }
        for _, request := range result.Requests {
            e.Scheduler.Submit(request)
        }
    }
}

func createWorker(in chan Request, out chan ParseResult) {

    go func() {
        for {
            request := <-in
            // 这里调用获取url结果
            result, err := worker(request)
            if err != nil {
                continue
            }
            out <- result
        }
    }()
}

package scheduler

import (
    "crawler/engine"
)

type SimpleScheduler struct {
    workerChan  chan engine.Request
}


func (s *SimpleScheduler) Submit (r engine.Request){
    // 这里为什么要开一个协程? 视频上讲,不开协程会循环堵塞
    go func() {s.workerChan <- r}()
}

func (s *SimpleScheduler) ConfigurMasterWorkerChan(c chan engine.Request){
    //  这里给workeChan 赋值不懂是为什么
    s.workerChan = c
}

e.Scheduler.ConfigurMasterWorkerChan(in) 这里,为什么注释掉这句话,程序无法运行呢。 s.workerChan = c 这里的赋值有什么意义呢。

request := <-in // 这里调用获取 url 结果 在这里,好像没有输入 in 的代码,但是为什么这里能执行呢

更多原创文章干货分享,请关注公众号
  • 加微信实战群请加微信(注明:实战群):gocnio

关键你要理解下面的结构的意思

type ConcurrentEngine struct {
    Scheduler   Scheduler
    WorkerCount int
}

type Scheduler interface {
    Submit(Request)
    ConfigurMasterWorkerChan(chan Request)
}

ConcurrentEngine 中定义了一个 Scheduler 接口类型的字段,估计你是接口这块没有理解。见意你看一下 go 接口这方面的资料。

e := engine.ConcurrentEngine{
        Scheduler:&amp;scheduler.SimpleScheduler{}, // 这里为什么要用取地址?
        WorkerCount:100,
    }

上面这段赋值过程,SimpleScheduler 实现了 ConcurrentEngine 中的 Scheduler 接口。

 in := make(chan Request)
    out := make(chan ParseResult)
e.Scheduler.ConfigurMasterWorkerChan(in) //  这一句话看了很久,一直不懂是什么意思
func (s *SimpleScheduler) ConfigurMasterWorkerChan(c chan engine.Request){
    //  这里给workeChan 赋值不懂是为什么
    s.workerChan = c
}

上面这两段的意义就是创建一个 chan 变量,workerChan 在使用前一定要 make 一下,否则不能用的。

有完整源码吗?可以分享一下吗?

先下结论: 这段代码核心内容就是 生产者-消费者 模式得变形.

题主 没理解 也不是不可以, 因为这段 花哨 得实现内强加了一个 type Scheduler interface 部分, 这个看起来干了点 脱裤子放屁 得事情.

因为 没有全部代码 所以只能下这个结论.

可以把这部分去掉, 揉合到 ConcurrentEngine 内就好理解了.

回答一下你的第一个问题:

e := engine.ConcurrentEngine{
    Scheduler:&amp;scheduler.SimpleScheduler{}, // 这里为什么要用取地址?
    WorkerCount:100,
}

关于 SimpleScheduler 这个 struct, 从下面这段代码可以看出,是 SimpleScheduler 的指针 implement 了 Scheduler 这个 interface,因为 Submit 和 ConfigureMasterWorkerChan 的 receiver 都是*SimpleScheduler:

func (s *SimpleScheduler) Submit (r engine.Request){
    // 这里为什么要开一个协程? 视频上讲,不开协程会循环堵塞
    go func() {s.workerChan &lt;- r}()
}

func (s *SimpleScheduler) ConfigurMasterWorkerChan(c chan engine.Request){
    //  这里给workeChan 赋值不懂是为什么
    s.workerChan = c
}

所以当我们 instantiate 一个 Scheduler 的实例的时候,应该使用一个 SimpleScheduler 的指针来初始化。 所以这里使用了&,代表一个指向 SimpleScheduler 对象的指针。

有趣的是,如果你使用值而不用指针来做 receiver 的话,比如将上述代码改成:

func (s SimpleScheduler) Submit (r engine.Request){
    // 这里为什么要开一个协程? 视频上讲,不开协程会循环堵塞
    go func() {s.workerChan &lt;- r}()
}

func (s SimpleScheduler) ConfigurMasterWorkerChan(c chan engine.Request){
    //  这里给workeChan 赋值不懂是为什么
    s.workerChan = c
}

这种情况下,使用指针或者值来初始化这个实例都是可以的,因为在 go 的设定里,可以直接使用指针来 call 一个对象的 function,编译器会自动 dereference 这个指针。

c 基础还是蛮重要的

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册