zoukankan      html  css  js  c++  java
  • 伴鱼面试题

    1 用time.After和context实现,两个方法本质上都是用了context或chan进行传递信息,如果go协程结束了,则用cancel关闭协程或往channel中传入值,同时用case time.After进行阻塞,若go协程超时了,则会走case time.After通道,

    两种方法,第一种是go协程里嵌套了go协程,第二种是先启动n个go协程去运行,再启动n个go协程去监控,

    package main
    import (
        "context"
        "fmt"
        "sync"
        "time"
    )
    //实现从三个网站爬取数据并用map保存的功能,如果协程超过1秒,则直接返回,
    // 1 用time.After结合context实现,
    // https://shockerli.net/post/golang-select-time-implement-timeout/
    var res = make(map[string]string)
    var rw sync.RWMutex
    func writeMap(ctx context.Context, key string, cancel context.CancelFunc) {
        rw.Lock()
        res[key] = (" " + key + " 网站爬取结果")
        rw.Unlock()
        // 写这个是为了节省时间,因为有可能go协程的运行时间小于WithTimeout中所给的时间,
        // 导致程序已经取完数据了,go协程仍然没有结束,
        cancel()
    }
    // 这里必须要开启两个go协程,一个用于运行爬虫程序,另一个用于监控时间,其中一个case是time.After即超时的channel,
    // 另一个是go协程结束后,会close ctx.Done,或者在规定的WithTimeout时间内go协程没有结束的话,WithTimeout内部会close ctx.Done
    func doSomething(key string) {
        ctx, cancel := context.WithTimeout(context.Background(), time.Second * 4)
        // 这个是为了防止忘记关闭协程,导致内存泄漏,
        defer cancel()
        go writeMap(ctx, key, cancel)
        select {
        case <- ctx.Done():
            fmt.Println(key + "网站爬取完毕!!!")
        // 注意这个time.After返回的是一个time类型的chan,所以这里可以这样写,
        case <-time.After(time.Second * 5):
            fmt.Println(key + "网站爬取超时!!!")
        }
    }
    func main() {
        var url = []string{
            "www.baidu.com",
            "www.123.com",
            "www.456.com",
        }
        for _, num := range url {
            go doSomething(num)
        }
        time.Sleep(time.Second * 8)
        fmt.Println(res)
    }
    View Code

    用time.After和chan实现

    // 只用time.After实现,这个方法的关键是当goroutine结束时,向chan通道中传入一个string,之后再用一个case去读取,
    // 如果能读到,则说明没有超时,否则走超时的case,
    var res sync.Map
    var wg sync.WaitGroup
    // 需要缓冲,不然阻塞
    var sign = make(chan string, 3)
    func writeMap(key string) {
        res.Store(key, key+" 网站爬虫结果")
    }
    func doSomething(key string) {
        writeMap(key)
        sign <- key
    }
    func doSomething1(key string) {
        for {
            writeMap(key)
        }
        sign <- key
    }
    func main() {
        var url = []string{
            "www.baidu.com",
            "www.123.com",
            "www.456.com",
        }
        for index, num := range url {
            if index == 2 {
                go doSomething1(num)
            } else {
                // 用死循环查看超时的情况,
                go doSomething(num)
            }
        }
        wg.Add(len(url))
        for _, num := range url {
            go func() {
                defer wg.Done()
                select {
                case r := <-sign:
                    fmt.Println(r + "网站爬取完毕!!!")
                    return
                // 注意这个time.After返回的是一个time类型的chan,所以这里可以这样写,
                case <-time.After(time.Second * 3):
                    fmt.Println(num + "网站爬取超时!!!")
                    return
                }
            }()
        }
        wg.Wait()
    }
    View Code

    参考:https://shockerli.net/post/golang-select-time-implement-timeout/ 这是类似第二种方法,只不过只有一个goroutine,

  • 相关阅读:
    GFS读后笔记
    BigTable读后笔记
    恢复系统基础理论
    事务基础理论
    ARIES算法简介
    怎么快速构建自己的C/C++程序?——有关编译、静态链接和SCons
    lua学习笔记
    运行时动态伪造vsprintf的va_list
    11月30日站立会议
    11月29号站立会议
  • 原文地址:https://www.cnblogs.com/xxswkl/p/14255855.html
Copyright © 2011-2022 走看看