
由于业务需要,服务器通过后台可自定义时间段轮询通过 websocket 向前端发送数据,但是运行一段时间后报错:
| goroutine 2 [chan send, 20 minutes]: | | serve_api/internal/app/v1/service.workers.func1(0xc0001b4480, 0xc000026980, 0x12) Users/jssmac/Documents/project/golang/serve_api/internal/app/v1/service/isv.go:60 +0x63| //读取可查询数据列表 func GetIsvList() (interface{}, error) { var ( newList []interface{} list []models.LsvInfo ) models.Find(&list, map[string]interface{}{"status": 1}) if len(list) > 0 { //等待组完成 var wg sync.WaitGroup //声明等待数 wg.Add(len(list)) for _, item := range list { go func(name string) { var c1 = worker(name) n := <-c1 newList = append(newList, n) wg.Done() }(item.Item) } wg.Wait() return newList, nil } else { return models.Array, nil } } // chan func worker(name string) chan interface{} { out := make(chan interface{}) go func(i string) { for { data := getIsvInfo(i) out <- data //原文件 isv.go:60 指向本行 } }(name) return out } //获取 ISV 数据 func getIsvInfo(name string) *Result { urls := "http://192.168.1.188:8000/isv/" params := url.Values{ "name": []string{name}, } res, _ := request("GET", urls, []byte(params.Encode())) regeo := new(Result) json.Unmarshal(res, ®eo) return regeo } //request 请求包装 func request(method, url string, data []byte) (body []byte, err error) { if method == "GET" { url = fmt.Sprint(url, "?", string(data)) data = nil } client := http.Client{Timeout: 10 * time.Second} req, err := http.NewRequest(method, url, bytes.NewBuffer(data)) if err != nil { return body, err } resp, err := client.Do(req) if err != nil { return nil, err } body, err = ioutil.ReadAll(resp.Body) defer resp.Body.Close() if err != nil { return body, err } return body, err } 1 asAnotherJack 2020-06-02 14:34:24 +08:00 worker 里为什么又开了一个 goroutine,而且这个 goroutine 里死循环的意义是什么,这边循环不断地写 out channel,但是外边只取了一次,第二次 send 就一直阻塞了 |
2 tiedan 2020-06-02 14:43:09 +08:00 worker 为什么是死循环啊? |
3 jss OP @asAnotherJack 那我不用 go func ,直接 for { data := getAspenPoint(name) out <- data } 吗? |
5 asAnotherJack div class="badges"> 2020-06-02 15:30:51 +08:00 @jss #3 外边调用 worker 的时候已经在一个子协程里了,里边没必要再开一个了,也不需要用一个 channel 返回结果,直接 return getIsvInfo(name)。 |
6 BlackBerry999 2020-06-02 15:41:51 +08:00 把 out := make(chan interface{})放到 worker(name string)里面?? |
7 jss OP @asAnotherJack 好的,我试试看 |
8 useben 2020-06-02 16:36:40 +08:00 ```go func main() { var ( sum = 100000 wg sync.WaitGroup ) rs := make(chan bool, sum) for i := 0; i < sum; i++ { wg.Add(1) go func(i int) { defer wg.Done() ping(rs, i, "http://www.baidu.com") }(i) } wg.Wait() close(rs) sl := make([]interface{}, 0, sum) for v := range rs { sl = append(sl, v) } fmt.Println(sl) } func ping(rs chan bool, i int, urlStr string) { //reqquest url fmt.Println(i, urlStr) rs <- true } ``` 模拟你的需求写了个 |
9 asAnotherJack 2020-06-02 16:50:26 +08:00 你的代码还有个问题,newList = append(newList, n) 这句代码在多个协程里 append newList 这个共享变量了,会有并发问题,可以把所有的结果先放入一个 channel,在 channel 的消费端统一 append 。 |
11 jss OP @asAnotherJack 好的,你看看 8 楼代码,我按照他 @useben 这种思路写? |
12 asAnotherJack 2020-06-02 17:28:09 +08:00 @jss #11 可以的 |
13 sonxzjw 2020-06-03 08:25:46 +08:00 只看错误提示,大概意思是你这个送消息到 chan 的操作,阻塞了 20 分钟了,怀疑运行时死循环你自个儿检查下。 |