2 回答

TA貢獻1780條經驗 獲得超4個贊
你陷入僵局是因為你繼續select
反對那些沒有違約的跟蹤器。您的內部for
循環每次都會迭代所有跟蹤器,其中包括已完成且永遠不會發送另一條消息的跟蹤器。解決這個問題的最簡單方法是 empty default
,這也會使它們在現實生活中表現得更好,因為它們并非都以相同的速度運行,但它確實會將其變成一個會消耗更多 CPU 的緊密循環。
你WaitGroup
什么都不做;你正在調用Wait
一個 goroutine,但當它返回時什么也不做,而且你永遠不會調用Done
它正在跟蹤的 goroutine,所以它永遠不會返回。相反,您單獨跟蹤Complete
收到的消息數量并使用它而不是 WaitGroup;目前尚不清楚為什么以這種方式實施。
修復兩者可解決所述問題:https ://go.dev/play/p/do0g9jrX0mY
但是,這可能不是正確的方法。不可能用一個人為的例子來說明正確的方法是什么;如果這個例子就是它需要做的全部,你不需要任何邏輯,你可以把你的打印語句放在工作人員中,只使用一個等待組而不使用任何通道就可以完成它。假設你實際上正在對結果做某事,你可能想要一個單一的Completed
通道和一個Error
由所有工作人員共享的單一通道,并且可能需要一個完全不同的機制來跟蹤進度,比如你可以從中讀取的原子 int/float想知道目前的進展。那么你不需要嵌套循環的東西,你只需要一個循環select
從共享頻道讀取消息。這完全取決于打算使用此代碼的上下文。

TA貢獻1856條經驗 獲得超5個贊
我想出了這種方法,它適用于我的需要:
package main
import (
"errors"
"fmt"
"strings"
"sync"
"time"
)
type ProgressTracker struct {
Progress int
Error error
Completed bool
Url string
}
/**
This method sleeps for 1 second and sends progress (in %) in each iteration to Progress channel
For .net sites on 3rd iteration fail with error
When everything is completed, send a message to Complete channel
*/
func work(url string, tracker chan ProgressTracker) {
var internalTracker = ProgressTracker{
Url: url,
}
tracker <- internalTracker
fmt.Printf("processing url %s\n", url)
for i := 1; i <= 5; i++ {
if url == "google.com" {
time.Sleep(time.Second * 3)
}
time.Sleep(time.Second)
if i == 3 && strings.HasSuffix(url, ".net") {
internalTracker.Error = errors.New("error for .net sites")
internalTracker.Completed = true
tracker <- internalTracker
return
}
progress := 20 * i
internalTracker.Progress = progress
internalTracker.Completed = false
tracker <- internalTracker
}
internalTracker.Completed = true
tracker <- internalTracker
}
func main() {
var urls = []string{"google.com", "youtube.com", "someurl.net"}
var tracker = make(chan ProgressTracker, len(urls))
var wg sync.WaitGroup
wg.Add(len(urls))
for _, url := range urls {
go func(workUrl string) {
defer wg.Done()
work(workUrl, tracker)
}(url)
}
go func() {
wg.Wait()
close(tracker)
fmt.Printf("After wg wait")
}()
var completed = 0
for completed < len(urls) {
select {
case t := <-tracker:
if t.Completed {
fmt.Printf("Processing for %s is completed!\n", t.Url)
completed = completed + 1
} else {
fmt.Printf("Processing for %s is in progress: %d\n", t.Url, t.Progress)
}
if t.Error != nil {
fmt.Printf("Url %s has errors %s\n", t.Url, t.Error)
}
}
}
}
在這里,我ProgressTracker作為通道傳遞(中的字段ProgressTracker被聲明為簡單字段,而不是通道)并且在來自工作函數的每個事件上返回正在發生的事情的完整狀態(如果進度增加 - 設置新值并將結構返回通道,如果發生錯誤 - 設置錯誤并返回結構等)。
- 2 回答
- 0 關注
- 114 瀏覽
添加回答
舉報