2017-12-29 35 views
0

저는 Goroutine으로 함수를 호출하고 WaitGroup을 사용하여 공유 스캐너가 모두 종료되기 전에 닫는 것을 방지합니다. myfunc() 함수는 파일을 반복 실행합니다. 필자는이 파일을 메모리 맵핑하고 매번 디스크에서 읽는 것의 I/O 문제가 아니라 모든 goroutine 사이에서 공유하려고했습니다. 이 접근 방식이 작동 할 것이라고 나는 들었다. in an answer to another question. 그러나이 기능은 독립적으로 잘 작동했지만 동시에 작동하지는 않습니다. 혼동되는패닉 : 런타임 오류 : goroutine으로 동시에 실행될 때 슬라이스 경계가 범위를 벗어납니다

panic: runtime error: slice bounds out of range 

하지만 난 (안 슬라이스에서) Scan() 메서드를 호출 할 때 오류가 있습니다 : 나는 오류가 발생하고있다. 내가 원래 myfunc() 내부를 이동하기 (비효율적으로 그것을마다 재정/재 선언) 문제가 공유 크기지도에 대한 동시 액세스라고 생각하지만,

// ... 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" 
    } 
} 

여전히 같은 결과 : 여기

은 MWE입니다 오류는 Scan()을 호출하는 것과 관련이 있습니다. 내가 여기에 공포의 전체 스택 추적의 내가 in this answer.

을받은 지침을 따르도록하려고 해요 :

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 

라인 81 것은 :

go myfunc(&param, scanner, ch) 

라인 (113)은 다음과 같습니다

for scanner.Scan() { 

답변

1

실제로 소스 코드 Scan을 검토 한 후 ar은 스레드로부터 안전합니다. 당신은 스캐너 오프 읽어 하나의 루틴을함으로써이 문제를 해결받을 수 있으며, 다른 루틴의 수는 줄을 소비하고 처리 :

또한
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" 
    } 
} 

함수의 마지막 줄을 보내고 defer 아무 문제가 없습니다 있습니다; defer의 전체 점은 함수 본문의 어딘가에서 호출하여 함수가 반환 된 후에 호출된다는 것을 알 수 있습니다. WaitGroup을 사용하여 스캐너를 완료 할 때까지 기능이 복귀하지 않도록하기 때문에 즉시 닫기를 지연시킬 수 있습니다.

+0

이것은 실제로 스캔을 사용하는 적절한 방법이지만, 읽기 전에 시작 루틴을 작성하는 것이 좋습니다. 그렇지 않으면 아무것도 채우지 않아 채널을 채우고 교착 상태가 발생할 수 있습니다. 또한'wg.Done()'은 main에 있으면 안된다. – Verran

+0

바이트 채널 슬라이스 채널을 사용하는'myfunc '에 문제가있는 반면, 라인 채널은 문자열을 가져 오기로 선언 되었습니까? – Dan

+0

@veran 스캐너가 별도의 goroutine에 있기 때문에 교착 상태가되지 않습니다. 그것은 앉아서 소비자가 올 때까지 기다릴 것이다. @Dan, 바이트 슬라이스 및 문자열은 쉽게 변환 가능하지만 alloc이 발생합니다. Scanner.Text()가 반환하는 것이기 때문에 string을 사용했다. – Adrian