由于业务需要,服务器通过后台可自定义时间段轮询通过 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 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 1
```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 1
你的代码还有个问题,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 分钟了,怀疑运行时死循环你自个儿检查下。
|