2 回答
TA貢獻(xiàn)1784條經(jīng)驗(yàn) 獲得超7個(gè)贊
我會(huì)使用單一渠道來傳達(dá)結(jié)果,因此收集結(jié)果要容易得多,而且它會(huì)根據(jù)其性質(zhì)自動(dòng)“縮放”。如果您需要識(shí)別結(jié)果的來源,只需使用包含來源的包裝器即可。是這樣的:
type Result struct {
? ? ID? ? ?string
? ? Result bool
}
為了模擬“真實(shí)”工作,工作人員應(yīng)該使用一個(gè)循環(huán)以迭代方式完成他們的工作,并且在每次迭代中他們應(yīng)該檢查取消信號(hào)。是這樣的:
func foo(ctx context.Context, pretendWorkMs int, resch chan<- Result) {
? ? log.Printf("foo started...")
? ? for i := 0; i < pretendWorkMs; i++ {
? ? ? ? time.Sleep(time.Millisecond)
? ? ? ? select {
? ? ? ? case <-ctx.Done():
? ? ? ? ? ? log.Printf("foo terminated.")
? ? ? ? ? ? return
? ? ? ? default:
? ? ? ? }
? ? }
? ? log.Printf("foo finished")
? ? resch <- Result{ID: "foo", Result: false}
}
在我們的示例中, thebar()是相同的,只是將所有foo單詞替換為bar。
現(xiàn)在執(zhí)行作業(yè)并提前終止其余作業(yè)(如果確實(shí)符合我們的預(yù)期),如下所示:
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
resch := make(chan Result, 2)
log.Println("Kicking off workers...")
go foo(ctx, 3000, resch)
go bar(ctx, 5000, resch)
for i := 0; i < cap(resch); i++ {
? ? result := <-resch
? ? log.Printf("Result of %s: %v", result.ID, result.Result)
? ? if !result.Result {
? ? ? ? cancel()
? ? ? ? break
? ? }
}
log.Println("Done.")
運(yùn)行此應(yīng)用程序?qū)⑤敵觯ㄔ贕o Playground上嘗試):
2009/11/10 23:00:00 Kicking off workers...
2009/11/10 23:00:00 bar started...
2009/11/10 23:00:00 foo started...
2009/11/10 23:00:03 foo finished
2009/11/10 23:00:03 Result of foo: false
2009/11/10 23:00:03 Done.
有些事情要注意。如果我們由于意外結(jié)果提前終止,cancel()函數(shù)將被調(diào)用,我們從循環(huán)中跳出??赡芷溆嗟墓ぷ魅藛T也同時(shí)完成他們的工作并發(fā)送他們的結(jié)果,這不會(huì)成為問題,因?yàn)槲覀兪褂昧司彌_通道,所以他們的發(fā)送不會(huì)阻塞并且他們會(huì)正確結(jié)束。此外,如果他們沒有同時(shí)完成,他們會(huì)檢查ctx.Done()他們的循環(huán),并且他們提前終止,所以 goroutines 被很好地清理了。
另請(qǐng)注意,上面代碼的輸出不打印bar terminated。這是因?yàn)?code>main()函數(shù)在循環(huán)后立即終止,一旦main()函數(shù)結(jié)束,它不會(huì)等待其他非maingoroutines 完成。如果應(yīng)用程序不會(huì)立即終止,我們也會(huì)看到該行被打印出來。time.Sleep()如果我們?cè)谀┪蔡砑右粋€(gè)main():
log.Println("Done.")
time.Sleep(3 * time.Millisecond)
輸出將是(在Go Playground上嘗試):
2009/11/10 23:00:00 Kicking off workers...
2009/11/10 23:00:00 bar started...
2009/11/10 23:00:00 foo started...
2009/11/10 23:00:03 foo finished
2009/11/10 23:00:03 Result of foo: false
2009/11/10 23:00:03 Done.
2009/11/10 23:00:03 bar terminated.
現(xiàn)在,如果您必須等待所有工作人員“正?!被颉疤崆啊苯Y(jié)束才能繼續(xù),您可以通過多種方式實(shí)現(xiàn)。
一種方法是使用sync.WaitGroup.?另一種方法是讓每個(gè)工作人員發(fā)送一個(gè)Result無論他們?nèi)绾谓Y(jié)束,并且Result可以包含終止條件,例如normalor?aborted。goroutinemain()可以繼續(xù)接收循環(huán),直到它n從 接收到值resch。如果選擇此解決方案,您必須確保每個(gè)工作人員發(fā)送一個(gè)值(即使發(fā)生恐慌)以main()在這種情況下(例如使用 using?defer)不阻塞。
TA貢獻(xiàn)1836條經(jīng)驗(yàn) 獲得超3個(gè)贊
我將針對(duì)您所說的內(nèi)容分享最簡單的模式。您可以將其擴(kuò)展到更復(fù)雜的場景。
func doStuff() {
// This can be a chan of anything.
msgCh := make(chan string)
// This is how you tell your go-routine(s) to stop, by closing this chan.
quitCh := make(chan struct{})
defer close(quitCh)
// Start all go routines.
for whileStart() {
go func() {
// Do w/e you need inside of your go-routine.
// Write back the result.
select {
case msgCh <- "my message":
// If we got here then the chan is open.
case <-quitCh:
// If we got here then the quit chan was closed.
}
}()
}
// Wait for all go routines.
for whileWait() {
// Block until a msg comes back.
msg := <-msgCh
// If you found what you want.
if msg == stopMe {
// It's safe to return because of the defer earlier.
return
}
}
}
- 2 回答
- 0 關(guān)注
- 166 瀏覽
添加回答
舉報(bào)
