zoukankan      html  css  js  c++  java
  • golang中time.After一直无法跳出select循环

    问题的代码如下,在for select 循环中,本想通过 time.After 设置超时时间,但一直无法退出。

    package main
    
    import (
    	"fmt"
    	"math/rand"
    	"time"
    )
    
    func main() {
    	ch := make(chan int)
    
    	go func() {
    		for v := range ch {
    			fmt.Println(v)
    		}
    	}()
    END:
    	for {
    		select {
    		case ch <- rand.Int():
    		case <-time.After(time.Second * 3):
    			fmt.Println("timeout")
    			break END
    		}
    		time.Sleep(time.Second)
    	}
    }
    

    控制台始终打印出从通道 ch 中读取的随机数,time.After 一直没能等到。

    原因是每次进入到 case <-time.After(time.Second * 3): 时,都会创建一个新的通道返回,所以这个 case 每次都在等待接收新的通道的数据,永远不可能等到。

    time.After 方法的源码如下:

    func After(d Duration) <-chan Time {
    	return NewTimer(d).C
    }
    

    每次调用都会通过 NewTimer 创建一个新的 Timer,并且 Timer.C 也是新创建的通道。

    func NewTimer(d Duration) *Timer {
    	c := make(chan Time, 1)
    	t := &Timer{
    		C: c,
    		r: runtimeTimer{
    			when: when(d),
    			f:    sendTime,
    			arg:  c,
    		},
    	}
    	startTimer(&t.r)
    	return t
    }
    

    为了解决上面的问题,我们可以把 time.After 放在 for 循环外面,来解决此问题。

    package main
    
    import (
    	"fmt"
    	"math/rand"
    	"time"
    )
    
    func main() {
    	ch := make(chan int)
    	timer := time.After(time.Second * 3)
    
    	go func() {
    		for v := range ch {
    			fmt.Println(v)
    		}
    	}()
    END:
    	for {
    		select {
    		case ch <- rand.Int():
    		case <-timer:
    			fmt.Println("timeout")
    			break END
    		}
    		time.Sleep(time.Second)
    	}
    }
    
  • 相关阅读:
    C#实体类对象修改日志记录
    C#中关于增强类功能的几种方式
    Elasticsearch入坑指南之RESTful API
    React入门实例
    .Net Core+Vue.js+ElementUI 实现前后端分离
    ElasticSearch入坑指南之概述及安装
    MySQL优化技巧
    RabbitMQ入门教程——路由(Routing)
    RabbitMQ入门教程——发布/订阅
    RabbitMQ入门教程——工作队列
  • 原文地址:https://www.cnblogs.com/jkko123/p/12733487.html
Copyright © 2011-2022 走看看