登录
首页 >  Golang >  Go问答

Go语言中的并发编程、协程同步和通道关闭

来源:stackoverflow

时间:2024-02-13 09:36:27 155浏览 收藏

本篇文章主要是结合我之前面试的各种经历和实战开发中遇到的问题解决经验整理的,希望这篇《Go语言中的并发编程、协程同步和通道关闭》对你有很大帮助!欢迎收藏,分享给更多的需要的朋友学习~

问题内容

熟悉并发性,因此开始编写一个带有并发调用的简单 ping cli(让我们忽略我并没有真正测量 ping)。

问题是,我无法在等待所有 goroutine 完成的同时正确关闭范围循环的通道。 如果我想同时调用 ping 函数,我无法将其与 waitgroups 同步,因为我将在所有 goroutine 完成之前到达 wg.wait() 行。

有没有办法保持 ping 调用并发并在完成后关闭通道,以便退出范围循环?

func main() {
    domain := flag.String("domain", "google.com", "the domain u want to ping")
    flag.Parse()

    sum := 0
    ch := make(chan int)

    go start_pings(*domain, ch)

    for elapsed := range ch {
        fmt.Println("Part time: " + strconv.Itoa(elapsed))
        sum += elapsed
    }

    avg := sum / 3
    fmt.Println("Average: " + strconv.Itoa(avg))
}

func start_pings(domain string, ch chan int) {
    var wg sync.WaitGroup
    for i := 0; i < 3; i++ {
        wg.Add(1)
        go ping(domain, ch, wg)
    }
    wg.Wait()
    close(ch)
}

func ping(domain string, ch chan int, wg sync.WaitGroup) {
    url := "http://" + domain
    start := time.Now()

    fmt.Println("Start pinging " + url + "...")

    resp, err := http.Get(url)
    elapsed := time.Now().Sub(start)

    if err != nil {
        fmt.Println(err)
        return
    }
    defer resp.Body.Close()

    ch <- int(elapsed)
    wg.Done()
}

正确答案


您不得复制 sync.WaitGroup文档明确指出:

首次使用后不得复制 waitgroup。

将指针传递给它:wg *sync.waitgroup。并调用 wg.done() 推迟!您还有其他 return 语句,这将导致 wg.done() 被跳过!

func start_pings(domain string, ch chan int) {
    var wg sync.waitgroup
    for i := 0; i < 3; i++ {
        wg.add(1)
        go ping(domain, ch, &wg)
    }
    wg.wait()
    close(ch)
}

func ping(domain string, ch chan int, wg *sync.waitgroup) {
    defer wg.done()
    url := "http://" + domain
    start := time.now()

    fmt.println("start pinging " + url + "...")

    resp, err := http.get(url)
    elapsed := time.since(start)

    if err != nil {
        fmt.println(err)
        return
    }
    defer resp.body.close()

    ch <- int(elapsed)
}

今天的 ide(和 golinter)对这种明显的误用发出了警告。为了避免此类错误,首先将 wg 声明为指针:

func start_pings(domain string, ch chan int) {
    wg := &sync.WaitGroup{} // Pointer!
    for i := 0; i < 3; i++ {
        wg.Add(1)
        go ping(domain, ch, wg)
    }
    wg.Wait()
    close(ch)
}

这样可以减少错误和误用的空间。

今天关于《Go语言中的并发编程、协程同步和通道关闭》的内容介绍就到此结束,如果有什么疑问或者建议,可以在golang学习网公众号下多多回复交流;文中若有不正之处,也希望回复留言以告知!

声明:本文转载于:stackoverflow 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>