恐慌:运行时错误:当作为goroutine并发运行时,切片超出范围

恐慌:运行时错误:当作为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

我将调用一个函数作为goroutine,并使用WaitGroup来防止在它们全部完成之前关闭共享扫描程序。
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(&param, 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(&param, 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"
    }
}