【发布时间】:2019-12-24 00:45:47
【问题描述】:
这是我遇到的一个有趣的情况。在使用 go-routines 进行一些数据操作之后,我需要从文件中读取,并根据我们发现的内容填充地图。这是简化的问题陈述和示例:
运行gen_data.sh生成需要的数据
#!/bin/bash
rm some.dat || :
for i in `seq 1 10000`; do
echo "$i `date` tx: $RANDOM rx:$RANDOM" >> some.dat
done
如果我使用loadtoDict.go 将some.dat 中的这些行读入map[int]string 而不使用go-routines,它会保持对齐。 (因为第一个和第二个词是一样的,见下面的o/p。)
在现实生活中,我确实需要在将线条加载到地图之前对其进行处理(昂贵),使用 go-routines 加快了我的字典创建速度,这是解决实际问题的重要要求。
loadtoDict.go
package main
import (
"bufio"
"fmt"
"log"
"os"
)
var (
fileName = "some.dat"
)
func checkerr(err error) {
if err != nil {
fmt.Println(err)
log.Fatal(err)
}
}
func main() {
ourDict := make(map[int]string)
f, err := os.Open(fileName)
checkerr(err)
defer f.Close()
fscanner := bufio.NewScanner(f)
indexPos := 1
for fscanner.Scan() {
text := fscanner.Text()
//fmt.Println("text", text)
ourDict[indexPos] = text
indexPos++
}
for i, v := range ourDict {
fmt.Printf("%d: %s\n", i, v)
}
}
跑步:
$ ./loadtoDict
...
8676: 8676 Mon Dec 23 15:52:24 PST 2019 tx: 17718 rx:1133
2234: 2234 Mon Dec 23 15:52:20 PST 2019 tx: 13170 rx:15962
3436: 3436 Mon Dec 23 15:52:21 PST 2019 tx: 17519 rx:5419
6177: 6177 Mon Dec 23 15:52:23 PST 2019 tx: 5731 rx:5449
注意第一个词和第二个词是如何“对齐”的。但是,如果我使用 go-routines 加载我的地图,就会出错:
async_loadtoDict.go
package main
import (
"bufio"
"fmt"
"log"
"os"
"sync"
)
var (
fileName = "some.dat"
mu = &sync.RWMutex{}
MAX = 9000
)
func checkerr(err error) {
if err != nil {
fmt.Println(err)
log.Fatal(err)
}
}
func main() {
ourDict := make(map[int]string)
f, err := os.Open(fileName)
checkerr(err)
defer f.Close()
fscanner := bufio.NewScanner(f)
indexPos := 1
var wg sync.WaitGroup
sem := make(chan int, MAX)
defer close(sem)
for fscanner.Scan() {
text := fscanner.Text()
wg.Add(1)
sem <- 1
go func() {
mu.Lock()
defer mu.Unlock()
ourDict[indexPos] = text
indexPos++
<- sem
wg.Done()
}()
}
wg.Wait()
for i, v := range ourDict {
fmt.Printf("%d: %s\n", i, v)
}
}
输出:
$ ./async_loadtoDict
...
11: 22 Mon Dec 23 15:52:19 PST 2019 tx: 25688 rx:7602
5716: 6294 Mon Dec 23 15:52:23 PST 2019 tx: 28488 rx:3572
6133: 4303 Mon Dec 23 15:52:21 PST 2019 tx: 24286 rx:1565
7878: 9069 Mon Dec 23 15:52:25 PST 2019 tx: 16863 rx:24234
8398: 7308 Mon Dec 23 15:52:23 PST 2019 tx: 4321 rx:20642
9566: 3489 Mon Dec 23 15:52:21 PST 2019 tx: 14447 rx:12630
2085: 2372 Mon Dec 23 15:52:20 PST 2019 tx: 14375 rx:24151
尽管使用互斥锁保护了摄取 ourDict[indexPos]。我希望我的地图索引与摄取尝试保持一致。
谢谢!
【问题讨论】:
-
多么不必要的复杂...索引不匹配的原因是即使您以相同的顺序创建 goroutine 并防止并发您有
MAX(9000) goroutines 等待,并且您无法控制它们恢复的顺序,索引代表执行顺序,而不是创建顺序 -
顺便说一下,您的代码是完全顺序的,只是不确定性。
-
除非我保留
MAX = 1,否则我会观察我上面报告的内容——这比让 go-routines 准备和填充我的map失败。我确实需要在将线条加载到地图之前对其进行处理,使用 go-routines 加快了我的字典创建速度,这是解决实际问题的重要要求。 -
在单个 goroutine 中添加对地图的访问,其他的只是准备数据(我想有一些数据操作,因为 if 只是像示例 goroutines 这样的传递实际上会使其变慢开销)。我将一个例子作为答案
-
正如我在回答中所说,您的信号量
sem不起作用,因为您对其进行了深度缓冲。当您设置MAX = 1时,您将其设置为一个条目,然后它就会起作用:它会强制您的每个衍生的 goroutine 等到前一个完成后才能开始。
标签: go