为什么添加time.sleep时我的程序为什么挂起?

时间:2019-07-19 18:47:47

标签: go

我正在尝试对我的程序进行测试,该程序将在设定的时间间隔内保存到磁盘。我的问题是我加了时间。无论测试时间长短,程序都将挂起。

我的预期行为是我允许它进入睡眠状态,并且go例程应该在后台运行,并保存到磁盘上。

这是正在测试中作为goroutine运行的功能。

nodeservice.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

对于引起此问题的原因的任何见解,将不胜感激。

2 个答案:

答案 0 :(得分:1)

函数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)

答案 1 :(得分:0)

go runRegistryBackup(reg, interval, kill)返回case <-time.Tick,无法从终止通道读取任何内容,因此主例程在kill <- true上阻塞。 我不完全了解您的程序的逻辑,因此让事情正常工作的最简单方法是使kill通道缓冲

kill := make(chan bool, 1)
相关问题