3 回答

TA貢獻(xiàn)1798條經(jīng)驗(yàn) 獲得超3個(gè)贊
您遇到了 Go 調(diào)度程序的一個(gè)棘手問題。答案是這os.Exit 確實(shí)會(huì)導(dǎo)致整個(gè)進(jìn)程退出,但是按照您的方式,goroutines 從未運(yùn)行。
可能發(fā)生的情況是 for 循環(huán)不斷向可用 goroutine 列表中添加新的 goroutine,但是由于整個(gè)進(jìn)程僅在一個(gè) OS 線程中運(yùn)行,因此 Go 調(diào)度程序從未真正安排過不同的 goroutine,只是繼續(xù)運(yùn)行它for 循環(huán)而無需運(yùn)行您生成的任何 goroutine。試試這個(gè):
package main
import "os"
func main() {
for {
go func() { os.Exit(0) }()
func() {}()
}
}
如果您在 Go Playground 上運(yùn)行它,它應(yīng)該可以工作(實(shí)際上,這是一個(gè)鏈接)。
好的,上面的代碼可以運(yùn)行而你的代碼不運(yùn)行的事實(shí)應(yīng)該很神秘。這樣做的原因是 Go 調(diào)度程序?qū)嶋H上是非搶占的。這意味著除非給定的 goroutine 自愿決定給調(diào)度程序運(yùn)行其他東西的選項(xiàng),否則其他任何東西都不會(huì)運(yùn)行。
顯然,您從未編寫過包含讓調(diào)度程序有機(jī)會(huì)運(yùn)行的命令的代碼。發(fā)生的情況是,當(dāng)您的代碼被編譯時(shí),Go 編譯器會(huì)自動(dòng)將這些插入到您的代碼中。這是上述代碼為何起作用的關(guān)鍵:goroutine 可能決定運(yùn)行調(diào)度程序的時(shí)間之一是調(diào)用函數(shù)時(shí)。因此,通過添加func() {}()調(diào)用(顯然什么都不做),我們?cè)试S編譯器添加對(duì)調(diào)度程序的調(diào)用,讓這段代碼有機(jī)會(huì)調(diào)度不同的 goroutine。因此,產(chǎn)生的 goroutine 之一運(yùn)行,調(diào)用os.Exit,然后進(jìn)程退出。
編輯:在編譯器內(nèi)聯(lián)調(diào)用的情況下,函數(shù)調(diào)用本身可能是不夠的(或者,在這種情況下,因?yàn)樗裁炊疾蛔龆耆珓h除它)。runtime.Gosched(),另一方面,保證工作。

TA貢獻(xiàn)1820條經(jīng)驗(yàn) 獲得超10個(gè)贊
您可以通過從函數(shù)返回來終止 goroutine。如果需要確保 goroutine 運(yùn)行完成,則需要等待 goroutine 完成。這通常是通過sync.WaitGroup, 或通過通道同步 goroutine 來完成的。
在您的示例中,您首先需要確保不可能產(chǎn)生無限數(shù)量的 goroutine。因?yàn)閙ain新的 goroutine之間沒有同步點(diǎn),所以不能保證os.Exit在主循環(huán)運(yùn)行時(shí)它們中的任何一個(gè)都會(huì)執(zhí)行調(diào)用。
等待任意數(shù)量的 goroutine 完成的通常方法是使用 a sync.WaitGroup,這將確保它們?cè)趍ain退出之前都已執(zhí)行。
wg := sync.WaitGroup{}
for i := 0; i < 10000; i++ {
wg.Add(1)
go func() { defer wg.Done() }()
}
wg.Wait()
fmt.Println("done")

TA貢獻(xiàn)1880條經(jīng)驗(yàn) 獲得超4個(gè)贊
實(shí)施死手或終止開關(guān)
package main
import (
"fmt"
"time"
"os"
)
const maxNoTickle = 50 // will bail out after this many no tickles
const maxWorking = 20 // pretendWork() will tickle this many times
const deadTicks = 250 // milliseconds for deadHand() to check for tickles
const reportTickles = 4 // consecutive tickles or no tickles to print something
var (
tickleMe bool // tell deadHand() we're still alive
countNoTickle int // consecutive no tickles
countGotTickle int // consecutive tickles
)
/**
* deadHand() - callback to kill program if nobody checks in after some period
*/
func deadHand() {
if !tickleMe {
countNoTickle++
countGotTickle = 0
if countNoTickle > maxNoTickle {
fmt.Println("No tickle max time reached. Bailing out!")
// panic("No real panic. Just checking stack")
os.Exit(0)
}
if countNoTickle % reportTickles == 0 {
// print dot for consecutive no tickles
fmt.Printf(".")
}
} else {
countNoTickle = 0
countGotTickle++
tickleMe = false // FIXME: might have race condition here
if countGotTickle % reportTickles == 0 {
// print tilda for consecutive tickles
fmt.Printf("~")
}
}
// call ourselves again
time.AfterFunc(deadTicks * time.Millisecond, deadHand)
}
/**
* init() - required to start deadHand
*/
func init() {
time.AfterFunc(250 * time.Millisecond, deadHand)
tickleMe = true
}
/**
* pretendWork() - your stuff that does its thing
*/
func pretendWork() {
for count := 0; count < maxWorking; count++ {
tickleMe = true // FIXME: might have race condition here
// print W pretending to be busy
fmt.Printf("W")
time.Sleep(100 * time.Millisecond)
}
}
func main() {
go workTillDone()
for {
// oops, program went loop-d-loopy
}
}
- 3 回答
- 0 關(guān)注
- 271 瀏覽
添加回答
舉報(bào)