我已更新问题,以避免被指控发布XY问题。
以前的问题是:
我怎么知道何时在主goroutine上调用了runtime.Goexit?
我正在尝试编写一种方法,该方法通过调用runtime.Goexit()
然后在生成的goroutine中调用os.Exit()
来完成主goroutine上所有延迟的函数,然后再调用此退出方法。我的问题是我不知道runtime.Goexit()
何时完成。我有什么办法可以知道主goroutine何时完成?
已更新:让我详细说明我的用例。为TestMain
考虑以下模式:
func TestMain(m *testing.M) {
db.Create()
defer db.Drop()
os.Exit(m.Run())
}
在这种情况下,因为os.Exit会停止程序,所以永远不会删除数据库。我的目标是提出一个替代方案
退出函数,该函数执行TestMain
中所有延迟的函数。现在,我可以将所有内容移到另一个
这样的功能:
func realTestMain(m *testing.M) int {
db.Create()
defer db.Drop()
return m.Run()
}
func TestMain(m *testing.M) {
os.Exit(realTestMain(m))
}
但是,这种模式必须在我们的测试套件中强制执行,而且很难看且很难记住。我正在探索 是否可以使用一个帮助程序来编写我的设置/拆卸文件,如下所示:
func TestMain(m *testing.M) {
db.Create()
defer db.Drop()
helpers.SafeExit(m.Run())
}
借助这样的助手,我可以编写一个简单的检查来确保os.Exit永远不会出现在我们的测试套件中。
答案 0 :(得分:5)
如果要确保进行协调的清理不要,请使用runtime.Goexit
。没有可靠/确定性的机制可以使用这种退出方法。
请改为使用context.Context并将其传递给您相关的例程。您可以显式取消Context,而例程可以监视此类事件的状态并进行清理。
如果有一些关键任务需要在市电退出之前执行,则可以使用sync.WaitGroup。为每个执行例程添加到等待组。并在每个执行例程完成时在WaitGroup上执行Done()
。主例程最终可以在WaitGroup上Wait()
上运行,并且在知道所有清理任务已经完成的情况下,程序将退出,
示例:
func main() {
mainCtx, cancelFn := context.WithCancel(context.Background())
var wg sync.WaitGroup
wg.Add(1)
go func() { taskA(mainCtx, &wg) }()
wg.Add(1)
go func() { manager(mainCtx, &wg, cancelFn) }()
wg.Wait() // waiting for manager/task(s)
}
func taskA(ctx context.Context, wg *sync.WaitGroup) {
defer func() {
log.Println("defer(): taskA DONE!")
wg.Done() // must be called last
}()
t := time.NewTicker(300 * time.Millisecond)
for {
select {
case <-t.C:
log.Println("taskA is working:", time.Now())
case <-ctx.Done(): // are we done?
log.Println("taskA was canceled. Reason:", ctx.Err())
return
}
}
}
func manager(ctx context.Context, wg *sync.WaitGroup, cancelFn func()) {
defer func() {
log.Println("defer(): Manager DONE!")
wg.Done() // must be called last
}()
time.Sleep(time.Second) // give worker(s) some time to work...
cancelFn() // time to wrap up worker(s)!
}
答案 1 :(得分:2)
我有什么办法可以知道主goroutine何时完成?
是:程序停止后。
一旦主goroutine停止,您的程序即会终止,包括任何其他goroutine。因此,您将不得不重新设计,因为在主要goroutine退出后,什么也不会执行。
答案 2 :(得分:-2)
这是一个我可以在主goroutine的变量上使用终结器然后触发垃圾回收的解决方案:
package main
import (
"fmt"
"os"
"runtime"
"time"
)
type toBeCollected struct {
dummy bool // need to have a non-empty struct
}
func main() {
collectMe := new(toBeCollected)
runtime.SetFinalizer(collectMe, func(*toBeCollected) {
fmt.Println("now I'm really done")
os.Exit(0)
})
go func() {
for range time.NewTicker(time.Millisecond).C {
runtime.GC()
}
}()
runtime.Goexit()
}
奇怪的是,虽然我可以在本地运行它,但它在操场上却无法使用(请参见https://play.golang.org/p/bwf7fMu2w76)。