恐慌:运行时错误:当作为goroutine并发运行时,切片超出范围
我将调用一个函数作为goroutine,并使用WaitGroup来防止在它们全部完成之前关闭共享扫描程序。恐慌:运行时错误:当作为goroutine并发运行时,切片超出范围,go,concurrency,runtime-error,Go,Concurrency,Runtime Error,我将调用一个函数作为goroutine,并使用WaitGroup来防止在它们全部完成之前关闭共享扫描程序。myfunc()函数迭代文件。我想要内存映射这个文件并在所有goroutine之间共享它,而不是每次都从磁盘读取I/O阻塞点。我被告知这种方法可以工作,但是,虽然这个函数单独工作很好,但不能同时工作。我收到错误信息: panic: runtime error: slice bounds out of range 但是错误是当我调用Scan()方法(不是在片上)时,这很混乱 这里是一个MWE
myfunc()
函数迭代文件。我想要内存映射这个文件并在所有goroutine之间共享它,而不是每次都从磁盘读取I/O阻塞点。我被告知这种方法可以工作,但是,虽然这个函数单独工作很好,但不能同时工作。我收到错误信息:
panic: runtime error: slice bounds out of range
但是错误是当我调用Scan()
方法(不是在片上)时,这很混乱
这里是一个MWE:
// ... package declaration; imports; yada yada
// the actual Sizes map is much more meaningful, this is just for the MWE
var Sizes = map[int]string {
10: "Ten",
20: "Twenty",
30: "Thirty",
40: "Forty",
}
type FileScanner struct {
io.Closer
*bufio.Scanner
}
func main() {
// ... validate path to file stored in filePath variable
filePath := "/path/to/file.txt"
// get word list scanner to be shared between goroutines
scanner := getScannerPtr(&filePath)
// call myfunc() for each param passed
var wg sync.WaitGroup
ch := make(chan string)
for _, param := range os.Args[1:] {
wg.Add(1)
go myfunc(¶m, scanner, ch)
wg.Done()
}
// print results received from channel
for range os.Args[1:] {
fmt.Println(<-ch) // print data received from channel ch
}
// don't close scanner until all goroutines are finished
wg.Wait()
defer scanner.Close()
}
func getScannerPtr(filePath *string) *FileScanner {
f, err := os.Open(*filePath)
if err != nil {
fmt.Fprint(os.Stderr, "Error opening file\n")
panic(err)
}
scanner := bufio.NewScanner(f)
return &FileScanner{f, scanner}
}
func myfunc(param *string, scanner *FileScanner, ch chan<-string) {
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
// ... do something with line (read only)
// ... access shared Sizes map when doing it (read only)
ch <- "some string result goes here"
}
}
第81行是:
go myfunc(¶m, scanner, ch)
第113行是:
for scanner.Scan() {
实际上,在检查了
扫描源代码之后,它似乎不是线程安全的。您可以通过读取扫描器的一个例程来解决这个问题,任何数量的其他例程都会占用行并处理它们:
func main() {
// ... validate path to file stored in filePath variable
filePath := "/path/to/file.txt"
// get word list scanner to be shared between goroutines
scanner := getScannerPtr(&filePath)
defer scanner.Close()
// call myfunc() for each param passed
var wg sync.WaitGroup
ch := make(chan string)
lines := make(chan string)
go func() {
for scanner.Scan() {
lines <- scanner.Text()
}
close(lines)
}()
for _, param := range os.Args[1:] {
wg.Add(1)
go myfunc(param, lines, ch)
wg.Done()
}
// print results received from channel
for range os.Args[1:] {
fmt.Println(<-ch) // print data received from channel ch
}
// don't close scanner until all goroutines are finished
wg.Wait()
}
func myfunc(param string, lines chan []byte, ch chan<-string) {
for line := range lines {
line = strings.TrimSpace(line)
// ... do something with line (read only)
// ... access shared Sizes map when doing it (read only)
ch <- "some string result goes here"
}
}
func main(){
//…验证存储在filePath变量中的文件的路径
filePath:=“/path/to/file.txt”
//获取要在goroutine之间共享的单词列表扫描程序
scanner:=getScannerPtr(&filePath)
延迟扫描程序。关闭()
//为传递的每个参数调用myfunc()
var wg sync.WaitGroup
ch:=制造(成串)
行:=make(chan字符串)
go func(){
对于scanner.Scan(){
行这确实是使用scan的正确方法,但我建议在开始读取之前创建go例程,否则您可能会填充通道并创建死锁,因为没有任何东西会耗尽它
不应在main中。如果使用字节片通道,而lines通道被声明为使用字符串,那么使用字节片通道是否会有任何问题?@veran它不会死锁,因为扫描仪位于一个单独的goroutine中。它将坐在那里等待消费者出现。@Dan,字节片和字符串很容易转换,但它不会s因为一个alloc。我使用字符串是因为Scanner.Text()
返回的是字符串。@Adrian好的,这很有意义。再次感谢您的时间和帮助。@Adrian这对第一个goroutine有效,但由于某些原因,第一个goroutine之后的所有goroutine似乎都无法正确迭代行。不确定原因
func main() {
// ... validate path to file stored in filePath variable
filePath := "/path/to/file.txt"
// get word list scanner to be shared between goroutines
scanner := getScannerPtr(&filePath)
defer scanner.Close()
// call myfunc() for each param passed
var wg sync.WaitGroup
ch := make(chan string)
lines := make(chan string)
go func() {
for scanner.Scan() {
lines <- scanner.Text()
}
close(lines)
}()
for _, param := range os.Args[1:] {
wg.Add(1)
go myfunc(param, lines, ch)
wg.Done()
}
// print results received from channel
for range os.Args[1:] {
fmt.Println(<-ch) // print data received from channel ch
}
// don't close scanner until all goroutines are finished
wg.Wait()
}
func myfunc(param string, lines chan []byte, ch chan<-string) {
for line := range lines {
line = strings.TrimSpace(line)
// ... do something with line (read only)
// ... access shared Sizes map when doing it (read only)
ch <- "some string result goes here"
}
}