代码之家  ›  专栏  ›  技术社区  ›  Ayub Malik

对于带缓冲通道的环路

  •  3
  • Ayub Malik  · 技术社区  · 7 年前

    我正在尝试Go频道,但有一个问题,下面的简单程序无法终止。

    本质上,我想发出一些异步HTTP get请求,然后 等待 直到他们 全部的 已完成。我使用的是缓冲通道,但我不确定这是惯用的方式。

    func GetPrice(quotes chan string) {
        client := &http.Client{}
        req, _ := http.NewRequest("GET", "https://some/api", nil)
        req.Header.Set("Accept", "application/json")
        res, err := client.Do(req)
        if err != nil {
            panic(err)
        }
        defer res.Body.Close()
        body, err := ioutil.ReadAll(res.Body)
        quotes <- string(body)
    }
    
    func main() {
        const max = 3
        quotes := make(chan string, max)
        for i := 0; i < max; i++ {
            go GetPrice(quotes)
        }
    
        for n := range quotes {
            fmt.Printf("\n%s", n)
        }
    }
    

    程序成功打印了3个(最多)项目

    {"price":"1.00"}
    {"price":"2.00"}
    {"price":"3.00"}
    

    但之后会阻塞,永远不会退出。

    3 回复  |  直到 5 年前
        1
  •  6
  •   Kaveh Shahbazian    3 年前

    sync.WaitGroup 可用于在此等待所有goroutine,然后关闭 quotes 频道:

    func getPrice(quotes chan<- string, onExit func()) {
        go func() {
            defer onExit()
    
            req, _ := http.NewRequest("GET", "https://some/api", nil)
            req.Header.Set("Accept", "application/json")
    
            client := &http.Client{}
            res, err := client.Do(req)
            if err != nil {
                panic(err) // should be handled properly
            }
            defer res.Body.Close()
    
            body, err := ioutil.ReadAll(res.Body)
            quotes <- string(body)
        }()
    }
    
    func main() {
        const max = 3
        var wg sync.WaitGroup
    
        quotes := make(chan string, max)
        for i := 0; i < max; i++ {
            wg.Add(1)
            getPrice(quotes, func() { wg.Done() })
        }
    
        go func() {
            defer close(quotes)
            wg.Wait()
        }()
    
        for n := range quotes {
            fmt.Printf("\n%s", n)
        }
    }
    
        2
  •  0
  •   newbie master    7 年前

    另一种方法是:

    package main
    
    import (
        "fmt"
        "io/ioutil"
        "net/http"
    )
    
    func GetPrice(quotes chan string) {
        client := &http.Client{}
        req, _ := http.NewRequest("GET", "https://some/api", nil)
        req.Header.Set("Accept", "application/json")
        res, err := client.Do(req)
        if err != nil {
            panic(err)
        }
        defer res.Body.Close()
        body, err := ioutil.ReadAll(res.Body)
        quotes <- string(body)
    }
    
    func run(quotes chan string, quit chan bool, max int) {
        for num := range quotes {
            fmt.Println(num)
            max--
    
            if max == 0 {
                quit <- true
            }
        }
    }
    
    func main() {
        const max = 3
        quotes := make(chan string)
        quit := make(chan bool)
    
        go run(quotes, quit, max)
    
        for i := 0; i < max; i++ {
            go GetPrice(quotes)
        }
    
        <-quit
    }
    
        3
  •  0
  •   Miffa Young    6 年前
    func GetPrice(quotes chan string) {
        client := &http.Client{}
        req, _ := http.NewRequest("GET", "https://some/api", nil)
        req.Header.Set("Accept", "application/json")
        res, err := client.Do(req)
        if err != nil {
            panic(err)
        }
        defer res.Body.Close()
        body, err := ioutil.ReadAll(res.Body)
        quotes <- string(body)
    }
    
    func Consumer(chan string){
        for n ,ok:= range quotes {
            if !ok {
                 break
            }
            fmt.Printf("\n%s", n)
        }
    }
    
    func main() {
        const max = 3
        quotes := make(chan string, max)
        for i := 0; i < max; i++ {
            go GetPrice(quotes)
        }
        go Consumer(quotes)
        close(quotes)
    }