3 回答

TA貢獻1772條經驗 獲得超6個贊
第一個問題是
wg.Add
always 必須在它所代表的 goroutine(s) 之外。如果不是,則wg.Wait
可能會在 goutine 實際開始運行(并被調用wg.Add
)之前調用該調用,因此會“認為”沒有什么可等待的。代碼的第二個問題是它等待例程完成的方式有多種。有渠道
WaitGroup
,有done
渠道。僅使用其中之一。哪一個還取決于如何使用 goroutines 的結果。在這里,我們來到下一個問題。第三個問題是收集結果。目前,該代碼僅打印/使用來自 goroutine 的單個結果。
for { ... }
在選擇周圍放置一個循環,如果通道關閉,則使用它return
來跳出循環。done
(請注意,您不需要在done
頻道上發送任何內容,關閉它就足夠了。)
改進版 0.0.1
所以這里的第一個版本(包括其他一些“代碼清理”)帶有done
用于關閉和WaitGroup
刪除的通道:
func main() {
ordersFile, err := os.Open(ordersFilename)
if err != nil {
log.Fatalln("Could not open file: " + ordersFilename)
}
orders := getOrderIDs(ordersFile)
files := Files{
filenames: getCSVsFromCurrentDir(),
}
var (
mu = new(sync.Mutex)
filenamesSize = len(files.filenames)
ch = make(chan map[string][]string, filenamesSize)
done = make(chan bool)
)
for i, filename := range files.filenames {
go func(currentFilename string, ch chan<- map[string][]string, i int, orders Orders, filenamesSize *int, mu *sync.Mutex, done chan<- bool) {
checkFile(currentFilename, orders, ch)
mu.Lock()
*filenamesSize--
mu.Unlock()
// TODO: This also accesses filenamesSize, so it also needs to be protected with the mutex:
if i == *filenamesSize {
done <- true
close(done)
}
}(filename, ch, i, orders, &filenamesSize, mu, done)
}
// Note: closing a channel is not really needed, so you can omit this:
defer close(ch)
for {
select {
case str := <-ch:
fmt.Printf("%+v\n", str)
case <-done:
return
}
}
}
改進版 0.0.2
但是,在您的情況下,我們有一些優勢。我們確切地知道我們啟動了多少個 goroutine,因此也知道我們期望有多少結果。(當然,如果每個 goroutine 返回一個當前代碼所做的結果。)這為我們提供了另一種選擇,因為我們可以使用另一個具有相同迭代次數的 for 循環來收集結果:
func main() {
ordersFile, err := os.Open(ordersFilename)
if err != nil {
log.Fatalln("Could not open file: " + ordersFilename)
}
orders := getOrderIDs(ordersFile)
files := Files{
filenames: getCSVsFromCurrentDir(),
}
var (
// Note: a buffered channel helps speed things up. The size does not need to match the size of the items that will
// be passed through the channel. A fixed, small size is perfect here.
ch = make(chan map[string][]string, 5)
)
for _, filename := range files.filenames {
go func(filename string) {
// orders and channel are not variables of the loop and can be used without copying
checkFile(filename, orders, ch)
}(filename)
}
for range files.filenames {
str := <-ch
fmt.Printf("%+v\n", str)
}
}
簡單很多,不是嗎?希望有幫助!

TA貢獻1796條經驗 獲得超4個贊
這段代碼有很多錯誤。
您使用 WaitGroup 錯誤。Add 必須在主 goroutine 中調用,否則有可能在所有 Add 調用完成之前調用 Wait。
在初始化與 Done() 調用不匹配的 WaitGroup 之后,有一個無關的 Add(1) 調用,因此 Wait 永遠不會返回(假設上面的點是固定的)。
您同時使用 WaitGroup 和 done 通道來表示完成。這充其量是多余的。
您正在讀取 filenamesSize 而沒有持有鎖(在
if i == *filenamesSize
語句中)。這是一個競爭條件。首先,這種
i == *filenamesSize
情況毫無意義。Goroutines 以任意順序執行,所以你不能確定 i == 0 的 goroutine 是最后一個減少 filenamesSize
這可以通過去掉大部分同步原語并在所有 goroutine 完成后簡單地關閉 ch 通道來簡化:
func main() {
ch := make(chan map[string][]string)
var wg WaitGroup
for _, filename := range getCSVsFromCurrentDir() {
filename := filename // capture loop var
wg.Add(1)
go func() {
checkFile(filename, orders, ch)
wg.Done()
}()
}
go func() {
wg.Wait() // after all goroutines are done...
close(ch) // let range loop below exit
}()
for str := range ch {
// ...
}
}

TA貢獻2019條經驗 獲得超9個贊
不是答案,而是一些不適合評論框的評論。
在這部分代碼中
func main() {
var (
ordersFile *os.File
files Files
orders Orders
err error
)
mu := new(sync.Mutex)
wg := &sync.WaitGroup{}
wg.Add(1)
最后一條語句是對 wg.Add 的調用,它看起來是懸空的。我的意思是我們很難理解什么會觸發所需的 wg.Done 對應部分。在沒有 wg.Done 的情況下調用 wg.Add 是一個錯誤,如果不以這樣的方式編寫它們很容易出錯,我們無法立即找到它們。
在代碼的那部分,顯然是錯誤的
go func(currentFilename string, ch chan<- map[string][]string, i int, orders Orders, wg *sync.WaitGroup, filenamesSize *int, mu *sync.Mutex, done chan<- bool) {
wg.Add(1)
defer wg.Done()
考慮到當例程執行時,您將 1 添加到等待組,父例程繼續執行??催@個例子: https: //play.golang.org/p/N9Chaqkv4bd 主程序不等待等待組,因為它沒有時間遞增。
還有更多要說的,但我發現很難理解你的代碼的目的,所以我不確定如何在不重寫它的情況下進一步幫助你。
- 3 回答
- 0 關注
- 148 瀏覽
添加回答
舉報