第一次使用go,并尝试开始例程并WaitGroups
正在工作。
我有一个包含100行数据的CSV文件。 (101包括标题)
我有以下简单的代码:
package main
import (
"bufio"
"fmt"
"io"
"os"
"sync"
"time"
)
func main() {
start := time.Now()
numRows := 0
waitGroup := sync.WaitGroup{}
file, _ := os.Open("./data.csv")
scanner := bufio.NewScanner(file)
scanner.Scan() // to read the header
for scanner.Scan() {
err := scanner.Err()
if err != nil && err != io.EOF {
panic(err)
}
waitGroup.Add(1)
go (func() {
numRows++
waitGroup.Done()
})()
}
waitGroup.Wait()
file.Close()
fmt.Println("Finished parsing ", numRows)
fmt.Println("Elapsed time in seconds: ", time.Now().Sub(start))
}
当我运行时,numRows
输出每次在94到100之间波动。我希望每次都是100。如果我在包含10行数据的CSV上运行相同的代码,则每次都会输出10
。
在我看来,最后的几个例程并没有及时完成。
我已尝试过以下失败的内容:
CsvReader
代替Scanner
waitGroup.Add(1)
移至匿名func 我缺少什么?
答案 0 :(得分:7)
在不同的goroutine中同时修改单个变量是不安全的。您对numRows
的部分更新将会丢失,有时您的程序可能会崩溃。
使用互斥锁保护您的numRows
变量,或使用其中一个atomic函数以原子方式进行添加:
var numRows int32
// ...
go (func() {
atomic.AddInt32(&numRows, 1)
waitGroup.Done()
})()
答案 1 :(得分:1)
您如何处理此代码:
for scanner.Scan() {
err := scanner.Err()
if err != nil && err != io.EOF {
panic(err)
}
waitGroup.Add(1)
go (func() {
numRows++
waitGroup.Done()
})()
}
实际上所有工作都在一个主要的goroutine中完成,只有numRows
增量使用单独的goroutine。我认为这可以简化为简单的增量:
for scanner.Scan() {
err := scanner.Err()
if err != nil && err != io.EOF {
panic(err)
}
numRows++
}
如果要模拟并行解析和流水线操作,可以使用通道。只有一个goroutine负责计数器增量。每次当另一个goroutine想要增加计数器时 - 它会向该频道发送一条消息。