我有一个要向上旋转的节点,直到我决定停止它为止。我目前有一个Start方法在Contexts Done通道上阻塞,然后有一个Stop函数调用cancel,在我的测试中,我的Start似乎永远挂起,并且从未调用过Stop。我无法弄清楚为什么未调用Done信号并停止我的节点。
var (
// Ctx is the node's main context.
Ctx, cancel = context.WithCancel(context.Background())
// Cancel is a function used to initiate graceful shutdown.
Cancel = cancel
)
type (
Node struct {
database *store.Store
}
)
// Starts run the Node.
func (n *Node) Start() error {
var nodeError error
defer func() {
err := n.database.Close()
if err != nil {
nodeError = err
}
}()
<-Ctx.Done()
return nodeError
}
// Stop stops the node.
func (n *Node) Stop() {
Cancel()
}
我的测试是:
func TestNode_Start(t *testing.T) {
n, _ := node.NewNode("1.0")
err := n.Start()
n.Stop()
assert.NoError(t, err)
}
答案 0 :(得分:2)
您的代码有几个问题。让我们分解一下。
var (
// Ctx is the node's main context.
Ctx, cancel = context.WithCancel(context.Background())
// Cancel is a function used to initiate graceful shutdown.
Cancel = cancel
)
这些不应该是程序包变量。它们应该是实例变量-也就是说,成员或Node
结构。通过制作这些程序包变量,如果您有多个使用Node
的测试,它们将互相踩到脚趾,导致竞争状况并崩溃。因此,请执行以下操作:
type Node struct {
database *store.Store
ctx context.Context
cancel context.CancelFunc
}
接下来,我们看到您的Start()
方法中有一个延迟函数:
// Starts run the Node.
func (n *Node) Start() error {
var nodeError error
defer func() {
err := n.database.Close()
if err != nil {
nodeError = err
}
}()
/* snip */
这不符合您的期望。 Start()
返回后,它将关闭数据库连接-在任何可能的机会使用它之前。
相反,您应该在Stop()
方法中关闭数据库连接:
// Stop stops the node.
func (n *Node) Stop() error {
n.cancel()
return n.database.Close()
}
最后,您的Start()
方法将阻塞,因为它等待上下文取消,该上下文在调用Stop()
之前是不可能被取消的,只有在Start()
返回之后才被调用:
func (n *Node) Start() error {
/* snip */
<-Ctx.Done()
return nodeError
}
我完全没有想到将<-Ctx.Done
中包含Start()
的任何原因,因此我将其删除。
在我建议的所有更改中,您应该具有以下内容:
type Node struct {
database *store.Store
ctx context.Context
cancel context.CancelFunc
}
// Starts run the Node.
func (n *Node) Start() {
n.ctx, n.cancel = context.WithCancel(context.Background())
}
// Stop stops the node.
func (n *Node) Stop() error {
n.cancel()
return n.database.Close()
}
当然,这仍然没有解决是否使用ctx
的问题。由于您的原始代码未包含该代码,因此我也没有。