为什么/何时从另一个goroutine调用上下文取消函数会导致死锁?
我很难理解上下文取消函数的概念,在这一点上调用cancel函数会导致死锁 我有一个声明上下文的main方法,我将其cancel函数传递给两个goroutine为什么/何时从另一个goroutine调用上下文取消函数会导致死锁?,go,Go,我很难理解上下文取消函数的概念,在这一点上调用cancel函数会导致死锁 我有一个声明上下文的main方法,我将其cancel函数传递给两个goroutine ctx := context.Background() ctx, cancel := context.WithCancel(ctx) go runService(ctx, wg, cancel, apiChan) go api.Run(cancel, wg, apiChan, aviorDb) 我在服务函数中使用此上下文(取消上下文后停
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
go runService(ctx, wg, cancel, apiChan)
go api.Run(cancel, wg, apiChan, aviorDb)
我在服务函数中使用此上下文(取消上下文后停止的无限循环)。
我通过从另一个goroutine调用cancel函数来控制它。
runService是一个长时间运行的操作,看起来与此类似:
func runService(ctx context.Context, wg *sync.WaitGroup, cancel context.CancelFunc, apiChan chan string) {
MainLoop:
for {
// this is the long running operation
worker.ProcessJob(dataStore, client, job, resumeChan)
select {
case <-ctx.Done():
_ = glg.Info("service stop signal received")
break MainLoop
default:
}
select {
case <-resumeChan:
continue
default:
}
waitCtx, cancel := context.WithTimeout(context.Background(), time.Duration(sleepTime)*time.Minute)
globalstate.WaitCtxCancel = cancel
<-waitCtx.Done()
}
_ = dataStore.SignOutClient(client)
apiChan <- "stop"
wg.Done()
cancel()
}
它在开始时由api.Run方法设置,如下所示:
func Run(cancel context.CancelFunc, wg *sync.WaitGroup, stopChan chan string, db *db.DataStore) {
...
appCancel = cancel
...
}
api有一个停止函数,该函数调用cancel函数:
var appCancel context.CancelFunc
func requestStop(w http.ResponseWriter, r *http.Request) {
_ = glg.Info("endpoint hit: shut down service")
if globalstate.WaitCtxCancel != nil {
globalstate.WaitCtxCancel()
}
state := globalstate.Instance()
state.ShutdownPending = true
appCancel()
encoder := json.NewEncoder(w)
encoder.SetIndent("", " ")
_ = encoder.Encode("stop signal received")
}
当调用requestStop函数并因此取消上下文时,长时间运行的操作(worker.ProcessJob)立即停止,整个程序死锁。在执行下一行代码之前,代码跳转到gopark
,原因是waitReasonSemAcquire
。(这只是调试器)
上下文取消函数仅在这两个位置调用。
因此,runService goroutine似乎出于某种原因阻止了api.run goroutine获得锁
到目前为止,我的理解是cancel函数可以传递给不同的goroutine,调用它时没有同步问题
例如,当我调用WaitCtxCancel函数时,它不会导致死锁
我可以
- 将上下文替换为1缓冲通道,并发送消息以中断循环
- 使用我的全局状态结构和布尔值
appCancel()
到
似乎解决了这个问题,这让我更加困惑。带有全局变量的东西(
globalstate.WaitCtxCancel=cancel
,也许还有appCancel
本身)在我看来可能很有活力。也许这些锁周围有锁,而这些锁本身导致了死锁?这对我来说很有意义。它并不总是发生,并且直到现在才与全局变量一起发生。为了安全和确保我的代码正常工作,我已经用通道替换了我的两个上下文。全局变量(globalstate.WaitCtxCancel=cancel
,也许还有appCancel
本身)的内容在我看来很有可能很有活力。也许这些锁周围有锁,而这些锁本身导致了死锁?这对我来说很有意义。它并不总是发生,并且直到现在才与全局变量一起发生。为了安全起见,并确保我的代码正常工作,我已经用通道替换了我的两个上下文。
go appCancel()