2017-03-31 4 views
3

私は30,000文字列のスライスを持っています。このスライスを、例えば、スライスから3000の文字列を取る10個のゴルーチンに分割し、いくつかのデータを抽出して新しいスライスに挿入するにはどうすればよいですか?配列処理をゴルーチンに分割する方法は?

したがって、最終的には、それぞれ3000個の処理結果を持つ10個のスライスが作成されます。この問題を処理するパターンは何ですか?

私はthis articleを見ましたが、これらのパターンのどれが私のケースに当てはまるかはわかりません。

+2

なぜ10個のgoroutineを持っていて、できるだけ速く処理するのでしょうか? – JimB

答えて

2

チャネルを使用して、スライスから要素を読み取り、ファンアウトを使用して負荷を分散し、メッセージを渡します。その後、ゴルーチンの文字列を処理し、mutexを避けるために、結果を単一のゴルーチンに集めて戻します(ファンイン)。

最大並列同時ゴルーチンの数を設定できます。

スライスは、書き込み時にスレッドセーフではありません。

お役立ち情報:

https://blog.golang.org/pipelines https://talks.golang.org/2012/concurrency.slide#1 https://blog.golang.org/advanced-go-concurrency-patterns https://talks.golang.org/2013/advconc.slide#1

+1

ただし、スライスは読み取り専用の場合はスレッドセーフです。彼の質問は特に、彼がこのスライスを読んでいて、別のスライスに書き込んでいると述べています。結果が最後に結合された別々のスライスである限り、入力から読み取ることは同時に実行することができます。感謝@RayfenWindspear – RayfenWindspear

+0

。私は、 –

+2

であることを明確にしました。ファンインを使用して結果を収集するので、単一のゴルーチンだけが宛先スライスに書き込んでいます。ミューテックスの必要性を避けます。 – Kaedys

0

私はなぜ行くのルーチンを制限することで@JimBに同意します。しかし、それはあなたのお問い合わせですので、私はおそらくこのようなものでしょう。本当に各gorountineに3000個のアイテムを持たせたいのであれば、2次元スライスを作成する方が簡単かもしれません。 [[3000個のアイテム]、[3000個のアイテム]、..]その後、その2次元配列のインデックスごとに1つのルーチンプロセスがあります。そうでない場合にだけinit関数は、あなたを解析する場合は、2Dアレイ10個のアレイ3000個の要素を含む各..に出チャンク作成される方法2

import (
    "crypto/rand" 
    "fmt" 
    "log" 
    "sync" 
    "time" 
) 

var s []string 

// genetate some mock data 
func init() { 
    s = make([]string, 30000) 
    n := 5 
    for i := 0; i < 30000; i++ { 
     b := make([]byte, n) 
     if _, err := rand.Read(b); err != nil { 
      panic(err) 
     } 
     s[i] = fmt.Sprintf("%X", b) 
    } 
} 

func main() { 
    // set the number of workers 
    ch := make(chan string) 
    var mut sync.Mutex 
    counter := 0 

    // limit the number of gorountines to 10 
    for w := 0; w < 10; w++ { 
     go func(ch chan string, mut *sync.Mutex) { 
      for { 
       // get and update counter using mux to stop race condtions 
       mut.Lock() 
       i := counter 
       counter++ 
       mut.Unlock() 
       // break the loop 
       if counter > len(s) { 
        return 
       } 
       // get string 
       myString := s[i] 
       // to some work then pass to channel 
       ch <- myString 

      } 
     }(ch, &mut) 
    } 
    // adding time. If you play wiht the number of gorountines youll see how changing the number above can efficiency 
    t := time.Now() 
    for i := 0; i < len(s); i++ { 
     result := <-ch 
     log.Println(time.Since(t), result, i) 
    } 
} 

主10 METHOD 1 パッケージgorountinesを制限以下データそのようにして、それ以下のロジックではほとんど変更を加える必要はありません。

package main 

import (
    "crypto/rand" 
    "fmt" 
    "log" 
    "sync" 
) 

var chunkedSlice [10][3000]string 

// genetate some mock data 
// 2d array, each chunk has 3000 items in it 
// there are 10 chunks, 1 go rountine per chunk 
func init() { 
    n := 5 
    for i := 0; i < 10; i++ { 
     for j := 0; j < 3000; j++ { 
      b := make([]byte, n) 
      if _, err := rand.Read(b); err != nil { 
       panic(err) 
      } 
      chunkedSlice[i][j] = fmt.Sprintf("%X", b) 
     } 
    } 
} 

func main() { 
    // channel to send parsed data to 

    ch := make(chan string) 
    var wg sync.WaitGroup 

    // 10 chunks 
    for _, chunk := range chunkedSlice { 
     wg.Add(1) 
     // if defining the 2d array e.g [10][3000]string, you need to pass it as a pointer to avoid stack error 
     go func(ch chan string, wg *sync.WaitGroup, chunk *[3000]string) { 
      defer wg.Done() 
      for i := 0; i < len(chunk); i++ { 
       str := chunk[i] 
       // fmt.Println(str) 
       // parse the data (emulating) 
       parsedData := str 
       // send parsed data to the channel 
       ch <- parsedData 
      } 
     }(ch, &wg, &chunk) 
    } 
    // wait for all the routines to finish and close the channel 
    go func() { 
     wg.Wait() 
     close(ch) 
    }() 

    var counter int // adding to check that the right number of items was parsed 
    // get the data from the channel 
    for res := range ch { 
     log.Println(res, counter) 
     counter++ 
    } 
} 
+0

goroutineをmuckingするのではなく、入力リストを 'w'サブスライスに分割し、それぞれを処理する方が簡単かもしれません。または、ファンイン出力チャネルに加えて、ファンアウト入力チャネルを使用します。 – Kaedys

+0

私はそれを考えなかったことに同意します – reticentroot

関連する問題