Go语言竞态条件处理:如何确保main与attack耗时均正常输出?
Go协程竞态问题解决:确保defer函数执行并打印耗时(通道实现)
问题核心是:main函数读取到attack发送的通道值后立即退出,导致attack的defer timer函数还没来得及执行程序就终止了——Go程序会在main函数退出时立刻终止所有运行中的协程,不管协程有没有执行完。
以下是用通道实现的解决方案,确保main与attack的耗时都能稳定打印:
方案一:单个通道等待协程完全结束
修改逻辑,让attack的defer函数先打印耗时,再向通道发送信号,保证main只有在attack所有逻辑(包括defer)执行完才会退出:
package main import ( "fmt" "time" ) func timer(funcName string) func() { startTime := time.Now() return func() { fmt.Printf("%s took %v time to run \n", funcName, time.Since(startTime)) } } func main() { defer timer("main")() done := make(chan bool) evilNinja := "Tommy" go attack(evilNinja, done) <-done // 等待attack完全执行完毕(包括defer) } func attack(target string, done chan<- bool) { defer func() { timer("attack")() // 先打印耗时 done <- true // 再通知main可以退出 }() time.Sleep(time.Second) fmt.Println("Throwing ninja stars at ", target) }
方案二:保留攻击通知+新增协程结束通知
如果需要保留原有的“攻击完成”信号(比如要在main中处理后续逻辑),可以新增一个done通道,让main先处理攻击完成的逻辑,再等待协程完全结束:
package main import ( "fmt" "time" ) func timer(funcName string) func() { startTime := time.Now() return func() { fmt.Printf("%s took %v time to run \n", funcName, time.Since(startTime)) } } func main() { defer timer("main")() smokeSignal := make(chan bool) done := make(chan bool) evilNinja := "Tommy" go attack(evilNinja, smokeSignal, done) fmt.Println("Attack completed:", <-smokeSignal) // 处理攻击完成逻辑 <-done // 等待attack的defer执行完毕 } func attack(target string, smokeSignal chan<- bool, done chan<- bool) { defer func() { timer("attack")() done <- true }() time.Sleep(time.Second) fmt.Println("Throwing ninja stars at ", target) smokeSignal <- true // 通知攻击完成 }
方案原理
两种方案的核心都是让main函数等待attack协程的defer函数执行完毕。通过在defer代码块中发送通道信号,保证只有当attack的耗时打印完成后,main才会收到信号并退出程序,彻底消除了竞态条件。
内容的提问来源于stack exchange,提问作者Pruthviraj hawale
相关产品推荐
相关产品推荐

