go - 为什么我的程序在添加 time.sleep 时会挂起?
问题描述
我正在尝试对我的程序进行测试,该程序将在设定的时间间隔内保存到磁盘。我的问题是当我在测试中添加 time.Sleep 时,无论持续时间如何,程序都会挂起。
我的预期行为是我允许它休眠,并且 go 例程应该在后台运行并保存到磁盘的持续时间。
这是正在作为测试中的 goroutine 运行的有问题的函数。
节点服务.go
func runRegistryBackup(reg *NodeRegistry, interval int, killchan chan bool) {
log.SetPrefix("[Registry Backup]\t")
log.Printf("Intializing backup every %d seconds", interval)
select {
case <-time.Tick(time.Duration(interval) * time.Second):
log.Println("Backing up node registry to disk")
reg.write()
case <-killchan:
log.Println("Flushing data to disk")
reg.write()
defer func() { killchan <- true }()
//defer close(killchan)
return
}
}
这是测试。我评论了添加时将冻结程序的行
nodeservice_test.go
func TestRunRegistry(t *testing.T) {
// need registry, interval, bool chan
// setup
fi, _ := ioutil.TempFile("", "msg-serv-test-")
defer os.Remove(fi.Name())
reg := MakeNodeRegistry(fi.Name())
name := "test"
ip := "10.0.0.1"
port := "0"
tls := false
node1 := MakeNode(name, ip, port, tls)
reg.addNode(node1)
interval := 5
kill := make(chan bool)
// test the interval
go runRegistryBackup(reg, interval, kill)
// let run for a little
time.Sleep(time.Minute) // adding this line hangs the whole program
// test kill
kill <- true
<-kill
actReg := BuildRegistry(fi.Name())
for key, eval := range reg.Nodes {
val := actReg.Nodes[key]
if eval != nil && val != nil {
assert(t, *val, *eval)
} else {
t.Logf("Expected Hash: %d \t Expected Node: %v", key, eval)
t.Logf("Key %d not found for Node %v", key, val)
t.Fail()
}
}
}
这是日志的输出,显示间隔只运行一次然后挂起。
[Registry Backup] 2019/07/19 13:29:51 Intializing backup every 5 seconds
[Registry Backup] 2019/07/19 13:29:56 Backing up node registry to disk
任何有关此问题原因的见解将不胜感激。
解决方案
该函数runRegistryBackup
调用write()
一次然后退出。然后主 goroutine 阻止发送到kill
.
通过向函数添加循环来修复。在循环之前创建一次代码,并在返回时停止代码。
func runRegistryBackup(reg *NodeRegistry, interval int, killchan chan bool) {
log.SetPrefix("[Registry Backup]\t")
log.Printf("Intializing backup every %d seconds", interval)
t := time.NewTicker(time.Duration(interval) * time.Second)
defer t.Stop()
for {
select {
case <-t.C:
log.Println("Backing up node registry to disk")
reg.write()
case <-killchan:
log.Println("Flushing data to disk")
reg.write()
defer func() { killchan <- true }()
return
}
}
}
无缓冲kill
通道给程序增加了一些脆弱性。例如,如果runRegistryBackup
修改为在写入错误时退出并且 main 尝试在该错误之后终止备份,则 main 将永远阻塞。通过使通道缓冲来修复。
kill := make(chan bool, 1)
推荐阅读
- autosar - DEM 读取快照数据时如何获取事件 ID?[autosar][矢量]
- reactjs - 没有使用渲染道具定义道具
- python - 如何使用python从.txt文件中提取随机单词?
- python - 如何使用python从字符串中删除引用的单词/短语?
- javascript - OrientDB:如何检查服务器端函数中的查询结果是否为空?
- c# - 在现有 .NET Core MVC 项目中基于 URL 有条件地渲染 React 组件
- javascript - 使用变换缩放:矩阵():如何坚持左边缘(改变缩放中心)?
- java - Java TCP Ping IP 地址
- php - 如何将主键的值插入到插入查询中
- nativescript - 为什么模拟器和物理设备之间的 scollview 行为不同?