[请教] 运行一段时间后报: goroutine 10 [chan send, 20 minutes]

2020-06-02 14:03:02 +08:00
 jss

由于业务需要,服务器通过后台可自定义时间段轮询通过 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, &regeo)
	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
}
3270 次点击
所在节点    Go 编程语言
14 条回复
asAnotherJack
2020-06-02 14:34:24 +08:00
worker 里为什么又开了一个 goroutine,而且这个 goroutine 里死循环的意义是什么,这边循环不断地写 out channel,但是外边只取了一次,第二次 send 就一直阻塞了
tiedan
2020-06-02 14:43:09 +08:00
worker 为什么是死循环啊?
jss
2020-06-02 15:01:56 +08:00
@asAnotherJack 那我不用 go func ,直接 for { data := getAspenPoint(name) out <- data } 吗?
jss
2020-06-02 15:03:25 +08:00
@tiedan 那我这个 worker 要怎么写,能详细描述一下吗?
asAnotherJack
2020-06-02 15:30:51 +08:00
@jss #3 外边调用 worker 的时候已经在一个子协程里了,里边没必要再开一个了,也不需要用一个 channel 返回结果,直接 return getIsvInfo(name)。
BlackBerry999
2020-06-02 15:41:51 +08:00
把 out := make(chan interface{})放到 worker(name string)里面??
jss
2020-06-02 16:13:10 +08:00
@asAnotherJack 好的,我试试看
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
}
```

模拟你的需求写了个
asAnotherJack
2020-06-02 16:50:26 +08:00
你的代码还有个问题,newList = append(newList, n) 这句代码在多个协程里 append newList 这个共享变量了,会有并发问题,可以把所有的结果先放入一个 channel,在 channel 的消费端统一 append 。
jss
2020-06-02 16:55:42 +08:00
@useben 好的,我研究一下
jss
2020-06-02 16:58:24 +08:00
@asAnotherJack 好的,你看看 8 楼代码,我按照他 @useben 这种思路写?
asAnotherJack
2020-06-02 17:28:09 +08:00
@jss #11 可以的
sonxzjw
2020-06-03 08:25:46 +08:00
只看错误提示,大概意思是你这个送消息到 chan 的操作,阻塞了 20 分钟了,怀疑运行时死循环你自个儿检查下。
jss
2020-07-08 13:50:59 +08:00
@useben 老哥,这段代码在运行一个月后 又又又 出问题了,能帮我看看嘛

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/677833

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX