select
关于select的一个错误使用方法,请看下面的两份代码:
代码:
函数returnAfter3s
是一个执行时长为3s
的函数,主函数中设置了1s
的超时。期望执行发生超时,也就是正常来说,1s后应当打印case after 1s
。
两段代码的不同之处在于,左边的代码syncChan
是同步写入的,没有另起goroutine写入;右边的asyncChan
开启了一个新的goroutine写入。
执行结果:
左边的是returnAfter3s
这个函数先执行(3s时间),然后才是case after 1s
,这与我们期望的相反;右边则是符合期望的。
原因是什么?左边的代码实际上是在等待syncCh
写入,而不是读取。select语句会将所有的值先计算出来,即函数调用先于写入发生。正确的做法是,在一个goroutine中写入,然后在主线程中select读取。
代码附录
package demo
import (
"testing"
"time"
)
// go test -run TestTimeoutWithSyncChan -v demo/*.go
func TestTimeoutWithSyncChan(t *testing.T) {
var returnAfter3s = func() bool {
time.Sleep(3 * time.Second)
t.Logf("returnAfter3s finished")
return true
}
syncChan := make(chan bool)
select {
case syncChan <- returnAfter3s():
t.Logf("case after returnAfter3s")
case <-time.After(1 * time.Second):
t.Logf("case after 1s")
}
t.Logf("main wait 4s")
time.Sleep(4 * time.Second)
t.Logf("main end")
}
// go test -run TestTimeoutWithAsyncChan -v demo/*.go
func TestTimeoutWithAsyncChan(t *testing.T) {
var returnAfter3s = func() bool {
time.Sleep(3 * time.Second)
t.Logf("returnAfter3s finished")
return true
}
asyncChan := make(chan bool)
go func() {
asyncChan <- returnAfter3s()
}()
select {
case <-asyncChan:
t.Logf("case after returnAfter3s")
case <-time.After(1 * time.Second):
t.Logf("case after 1s")
}
t.Logf("main wait 4s")
time.Sleep(4 * time.Second)
t.Logf("main end")
}