rap*_*ock 3 concurrency timeout channel go
当且仅当我在select语句正在侦听的任何通道上没有收到任何信号时,如何在特定时间段内突破包含select语句的惯用Go for循环.
让我以一个例子来提出这个问题.
var listenCh <-chan string我正在听的频道.listenCh.listenCh在关闭我的操作(永久地断开for循环)之前,我想在两个连续信号之间等待最多10秒(精度并不严重).
func doingSomething(listenCh <-chan string) {
var mystr string
for {
select {
case mystr <-listenCh:
//dosomething
case /*more than 10 seconds since last signal on listenCh*/:
return
}
}
}
Run Code Online (Sandbox Code Playgroud)
我将如何以最有效的方式实现我的要求.
通常的退出通道技术time.After(time.Duration)似乎在一个循环后没有重置,因此即使存在连续的值流,整个程序也会在10秒内关闭.
我在SO上找到了问题的变体(但不是我想要的),但我没有看到我的特定用例.
前言:使用time.Timer是推荐的方式,time.After()此处的使用仅用于演示和推理.请使用第二种方法.
time.After()(不推荐用于此)如果你放入time.After()case分支,那将在每次迭代中"重置",因为每次都会返回一个新的通道,这样就可以了:
func doingSomething(listenCh <-chan string) {
for {
select {
case mystr := <-listenCh:
log.Println("Received", mystr)
case <-time.After(1 * time.Second):
log.Println("Timeout")
return
}
}
}
Run Code Online (Sandbox Code Playgroud)
(我在Go Playground上使用了1秒超时可测试性.)
我们可以测试它:
ch := make(chan string)
go func() {
for i := 0; i < 3; i++ {
ch <- fmt.Sprint(i)
time.Sleep(500 * time.Millisecond)
}
}()
doingSomething(ch)
Run Code Online (Sandbox Code Playgroud)
输出(在Go Playground上试试):
2009/11/10 23:00:00 Received 0
2009/11/10 23:00:00 Received 1
2009/11/10 23:00:01 Received 2
2009/11/10 23:00:02 Timeout
Run Code Online (Sandbox Code Playgroud)
time.Timer(推荐解决方案)如果从频道收到高速率,这可能会浪费一些资源,因为新的计时器是在引擎盖下创建和使用的time.After(),当它不再需要时,它不会神奇地停止并立即收集垃圾.在超时之前从通道收到值的情况.
一个更加资源友好的解决方案是time.Timer在循环之前创建一个,如果在超时之前收到一个值,则重置它.
这是它的样子:
func doingSomething(listenCh <-chan string) {
d := 1 * time.Second
t := time.NewTimer(d)
for {
select {
case mystr := <-listenCh:
log.Println("Received", mystr)
if !t.Stop() {
<-t.C
}
t.Reset(d)
case <-t.C:
log.Println("Timeout")
return
}
}
}
Run Code Online (Sandbox Code Playgroud)
测试和输出是一样的.在Go Playground尝试这个.