2017-12-29 23 views
0

私はgoroutineとして関数を呼び出し、WaitGroupを使用して共用スキャナがすべて終了する前に閉じないようにします。 myfunc()関数はファイルに対して反復処理を行います。私は、このファイルをメモリマップでマップし、毎回ディスクから読み込むというI/Oチョークポイントを持つのではなく、すべてのゴルーチン間で共有したかったのです。私はこのアプローチがうまくいくと言われましたin an answer to another question.しかし、この機能はスタンドアロンで機能していましたが、同時に動作していませんでした。混乱しているpanic:ランタイムエラー:同時にゴルーチンとして実行するとスライス境界が範囲外になる

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

私はもともと問題が共有サイズのマップへの同時アクセスだと思ったが、myfunc()内側に移動する(そして非効率的にそれを毎回再定義/再宣言は)まだ同じになりましたエラーは、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はスレッドセーフである必要があります。あなたは、スキャナのオフ読ん1つのルーチンを持つことでこれを回避することができ、および他のルーチンの任意の数は、行を消費し、それらを処理:

また
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" 
    } 
} 

は、関数の最後の行をINGのdeferにはポイントがありませんのでご注意します。 deferの全体の点は、関数の本体のどこかで呼び出すことであり、関数が返った後に呼び出されることがわかります。 WaitGroupを使用してスキャナが終了するまでファンクションが戻らないようにしているので、安全にすぐに閉じることができます。

+0

これは確かにスキャンを使用する適切な方法ですが、読み込みを開始する前にgoルーチンを作成することをお勧めします。そうしないと、チャネルをいっぱいにしてデッドロックが発生することがあります。また、 'wg.Done()'はメインにあるべきではありません。 – Verran

+0

バイトチャネルのスライスを扱う 'myfunc'に問題はありますか?linesチャネルは文字列を受け取ると宣言されましたか? – Dan

+0

@veranスキャナが別のゴルーチンにあるため、デッドロックしません。それは座り、消費者が現れるまで待つ。 @Dan、バイトスライスと文字列は簡単に変換可能ですが、それはallocを引き起こします。 'Scanner.Text()'が返すものなので、私は文字列を使いました。 – Adrian

関連する問題