在下面的代码中:
ctx, cancel := context.WithTimeout(req.Context(), 5000*time.Second)
// Wait for the response or timeout
select {
case <-ctx.Done():
log.Println("timeout, cancel work...")
// Cancel the request and wait for it to complete
// this will shutdown the go-routine immediately
tr.CancelRequest(req)
log.Println(<-ch)
case err := <-ch:
// do something
}
select
同时等待两个接收操作。
一个接收操作(<-ch
)是块操作
在执行select
块期间,是否在ctx.Done()
块中多次调用select
以验证ctx.Done()
是否返回通道?直到<-ch
被阻止...
对于语句中的所有情况,接收操作的通道操作数以及send语句的通道和右侧表达式仅被精确评估一次,按源顺序输入“ select”语句。
select
仅调用一次ctx.Done()
。它返回一个通道,并监视是否可以继续从该通道接收操作。
如果5000*time.Second
超时到期,或者取消了父上下文(req.Context()
)(例如超时或调用了其cancel()
函数),则将关闭ctx.Done()
返回的通道,因此可以继续接收(不再是障碍)。 Spec: Receive operator:
closed通道上的接收操作总是可以立即进行,在接收到任何先前发送的值后会产生元素类型的zero value。
通常,当您使用阻塞通道和超时功能时,可以将其置于for-select循环中。
ctx, cancel := context.WithTimeout(req.Context(), 5000*time.Second)
// Wait for the response or timeout
for {
select {
case <-ctx.Done():
log.Println("timeout, cancel work...")
// Cancel the request and wait for it to complete
// this will shutdown the go-routine immediately
tr.CancelRequest(req)
log.Println(<-ch)
break
case err := <-ch:
// do something
break
}
}
在这种情况下,<-ch
不再阻塞,并且选择多次调用ctx.Done()
。如果不使用for循环,则ctx.Done()
仅被调用一次(如@icza所指出的)