2 回答

TA貢獻1784條經驗 獲得超7個贊
我會使用單一渠道來傳達結果,因此收集結果要容易得多,而且它會根據其性質自動“縮放”。如果您需要識別結果的來源,只需使用包含來源的包裝器即可。是這樣的:
type Result struct {
? ? ID? ? ?string
? ? Result bool
}
為了模擬“真實”工作,工作人員應該使用一個循環以迭代方式完成他們的工作,并且在每次迭代中他們應該檢查取消信號。是這樣的:
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。
現在執行作業并提前終止其余作業(如果確實符合我們的預期),如下所示:
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.")
運行此應用程序將輸出(在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.
有些事情要注意。如果我們由于意外結果提前終止,cancel()函數將被調用,我們從循環中跳出??赡芷溆嗟墓ぷ魅藛T也同時完成他們的工作并發送他們的結果,這不會成為問題,因為我們使用了緩沖通道,所以他們的發送不會阻塞并且他們會正確結束。此外,如果他們沒有同時完成,他們會檢查ctx.Done()他們的循環,并且他們提前終止,所以 goroutines 被很好地清理了。
另請注意,上面代碼的輸出不打印bar terminated
。這是因為main()
函數在循環后立即終止,一旦main()
函數結束,它不會等待其他非main
goroutines 完成。如果應用程序不會立即終止,我們也會看到該行被打印出來。time.Sleep()
如果我們在末尾添加一個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.
現在,如果您必須等待所有工作人員“正?!被颉疤崆啊苯Y束才能繼續,您可以通過多種方式實現。
一種方法是使用sync.WaitGroup
.?另一種方法是讓每個工作人員發送一個Result
無論他們如何結束,并且Result
可以包含終止條件,例如normal
or?aborted
。goroutinemain()
可以繼續接收循環,直到它n
從 接收到值resch
。如果選擇此解決方案,您必須確保每個工作人員發送一個值(即使發生恐慌)以main()
在這種情況下(例如使用 using?defer
)不阻塞。

TA貢獻1836條經驗 獲得超3個贊
我將針對您所說的內容分享最簡單的模式。您可以將其擴展到更復雜的場景。
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 關注
- 144 瀏覽
添加回答
舉報