Ich rufe eine Funktion als Goroutine und mit einer WaitGroup, um zu verhindern, einen freigegebenen Scanner zu schließen, bevor sie alle fertig sind. Die myfunc()
Funktion iteriert über eine Datei. Ich wollte diese Datei im Speicher abbilden und sie zwischen allen Gououtines teilen, anstatt jedesmal den I/O-Chokepunkt des Lesens von der Platte zu haben. Mir wurde gesagt, dass dieser Ansatz funktionieren würde Während diese Funktion jedoch einwandfrei funktionierte, funktioniert es nicht gleichzeitig. Ich erhalte den Fehler:panic: Laufzeitfehler: Slice-Grenzen außerhalb des Bereichs, wenn gleichzeitig als Goroutine ausgeführt
panic: runtime error: slice bounds out of range
aber der Fehler ist, wenn ich die Scan()
Methode aufrufen (nicht auf einer Scheibe), die verwirrend ist. Hier
ist ein 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"
}
}
Ich dachte ursprünglich das Problem gleichzeitigen Zugriff auf den gemeinsam genutzten Größen Karte war, aber es innerhalb myfunc()
(und ineffizienten erneut deklariert/Neudefinition es jedes Mal) noch in Folge der gleichen bewegten Fehler, der mit dem Aufruf von Scan()
zu tun hat. Ich bin versucht, die Führung ich in this answer.
Hier ist der vollständige Stack-Trace der Panik empfangen zu folgen:
panic: runtime error: slice bounds out of range
goroutine 6 [running]:
bufio.(*Scanner).Scan(0xc42008a000, 0x80)
/usr/local/go/src/bufio/scan.go:139 +0xb3e
main.crack(0xc42004c280, 0xc42000a080, 0xc42001c0c0)
/Users/dan/go/src/crypto_ctf_challenge/main.go:113 +0x288
created by main.main
/Users/dan/go/src/crypto_ctf_challenge/main.go:81 +0x1d8
exit status 2
Zeile 81 ist:
go myfunc(¶m, scanner, ch)
Linie 113:
for scanner.Scan() {
Dies ist in der Tat der richtige Weg, um Scan zu verwenden, aber ich würde vorschlagen, die Go-Routinen zu erstellen, bevor Sie mit dem Lesen beginnen, andernfalls können Sie den Kanal füllen und einen Deadlock erstellen, da nichts es ablässt. Auch 'wg.Done()' sollte nicht in main sein. – Verran
Wird es irgendwelche Probleme geben, wenn 'myfunc' einen Byte-Slice-Kanal nimmt, während der Linien-Kanal deklariert wurde, um Strings zu nehmen? – Dan
@veran es wird nicht Deadlock, weil der Scanner in einer separaten Goroutine ist. Es wird sitzen und warten, bis die Verbraucher kommen. @Dan, Byte Slices und Strings sind leicht konvertierbar, aber es verursacht ein Alloc. Ich bin mit String gegangen, weil 'Scanner.Text()' das zurückgibt. – Adrian