Golang Go语言中 [请教] 运行一段时间后报 goroutine 10 [chan send, 20 minutes]

发布于 1周前 作者 bupafengyu 来自 Go语言

Golang Go语言中 [请教] 运行一段时间后报 goroutine 10 [chan send, 20 minutes]

由于业务需要,服务器通过后台可自定义时间段轮询通过 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
}

更多关于Golang Go语言中 [请教] 运行一段时间后报 goroutine 10 [chan send, 20 minutes]的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html

15 回复

worker 里为什么又开了一个 goroutine,而且这个 goroutine 里死循环的意义是什么,这边循环不断地写 out channel,但是外边只取了一次,第二次 send 就一直阻塞了

更多关于Golang Go语言中 [请教] 运行一段时间后报 goroutine 10 [chan send, 20 minutes]的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


worker 为什么是死循环啊?

那我不用 go func ,直接 for { data := getAspenPoint(name) out <- data } 吗?

那我这个 worker 要怎么写,能详细描述一下吗?

#3 外边调用 worker 的时候已经在一个子协程里了,里边没必要再开一个了,也不需要用一个 channel 返回结果,直接 return getIsvInfo(name)。

把 out := make(chan interface{})放到 worker(name string)里面??

好的,我试试看

go<br>func main() {<br> var (<br> sum = 100000<br> wg sync.WaitGroup<br> )<br> rs := make(chan bool, sum)<br> for i := 0; i &lt; sum; i++ {<br> wg.Add(1)<br> go func(i int) {<br> defer wg.Done()<br> ping(rs, i, "<a target="_blank" href="http://www.baidu.com" rel="nofollow noopener">http://www.baidu.com</a>")<br> }(i)<br> }<br><br> wg.Wait()<br> close(rs)<br><br> sl := make([]interface{}, 0, sum)<br> for v := range rs {<br> sl = append(sl, v)<br> }<br><br> fmt.Println(sl)<br>}<br><br>func ping(rs chan bool, i int, urlStr string) {<br> //reqquest url<br> fmt.Println(i, urlStr)<br> <br> rs &lt;- true<br>}<br>

模拟你的需求写了个

你的代码还有个问题,newList = append(newList, n) 这句代码在多个协程里 append newList 这个共享变量了,会有并发问题,可以把所有的结果先放入一个 channel,在 channel 的消费端统一 append 。

好的,我研究一下

好的,你看看 8 楼代码,我按照他 这种思路写?

只看错误提示,大概意思是你这个送消息到 chan 的操作,阻塞了 20 分钟了,怀疑运行时死循环你自个儿检查下。

老哥,这段代码在运行一个月后 又又又 出问题了,能帮我看看嘛

在Go语言中,看到类似 goroutine 10 [chan send, 20 minutes] 的日志输出,表明第10个goroutine在尝试向一个通道(channel)发送数据时,已经阻塞了20分钟。这通常意味着该goroutine等待的目标通道没有准备好接收数据,可能是因为:

  1. 接收端未启动或已停止:确保所有预期接收数据的goroutine都已正确启动,并且在发送数据前保持活跃状态。

  2. 缓冲区满:如果通道是有缓冲的,检查是否因为缓冲区已满而导致发送阻塞。可以考虑增加缓冲区大小,或者优化数据生产和消费的逻辑,避免生产速度远超过消费速度。

  3. 死锁:检查是否存在其他goroutine因某种原因也处于阻塞状态,导致整个系统陷入死锁。使用Go的race detector(通过go run -race)可以帮助检测数据竞争和潜在的死锁问题。

  4. 超时与取消:考虑实现超时机制或取消信号,使用context.Context来管理goroutine的生命周期和取消操作。

解决这类问题通常需要详细审查相关goroutine的启动逻辑、通道的使用方式以及数据处理流程。确保每个通道的使用都遵循了生产者-消费者模型的最佳实践,避免不必要的阻塞和死锁。如果问题复杂,可能需要逐步调试或增加日志输出,以更精确地定位问题根源。

回到顶部