私は30,000文字列のスライスを持っています。このスライスを、例えば、スライスから3000の文字列を取る10個のゴルーチンに分割し、いくつかのデータを抽出して新しいスライスに挿入するにはどうすればよいですか?配列処理をゴルーチンに分割する方法は?
したがって、最終的には、それぞれ3000個の処理結果を持つ10個のスライスが作成されます。この問題を処理するパターンは何ですか?
私はthis articleを見ましたが、これらのパターンのどれが私のケースに当てはまるかはわかりません。
私は30,000文字列のスライスを持っています。このスライスを、例えば、スライスから3000の文字列を取る10個のゴルーチンに分割し、いくつかのデータを抽出して新しいスライスに挿入するにはどうすればよいですか?配列処理をゴルーチンに分割する方法は?
したがって、最終的には、それぞれ3000個の処理結果を持つ10個のスライスが作成されます。この問題を処理するパターンは何ですか?
私はthis articleを見ましたが、これらのパターンのどれが私のケースに当てはまるかはわかりません。
チャネルを使用して、スライスから要素を読み取り、ファンアウトを使用して負荷を分散し、メッセージを渡します。その後、ゴルーチンの文字列を処理し、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
ただし、スライスは読み取り専用の場合はスレッドセーフです。彼の質問は特に、彼がこのスライスを読んでいて、別のスライスに書き込んでいると述べています。結果が最後に結合された別々のスライスである限り、入力から読み取ることは同時に実行することができます。感謝@RayfenWindspear – RayfenWindspear
。私は、 –
であることを明確にしました。ファンインを使用して結果を収集するので、単一のゴルーチンだけが宛先スライスに書き込んでいます。ミューテックスの必要性を避けます。 – Kaedys
私はなぜ行くのルーチンを制限することで@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++
}
}
goroutineをmuckingするのではなく、入力リストを 'w'サブスライスに分割し、それぞれを処理する方が簡単かもしれません。または、ファンイン出力チャネルに加えて、ファンアウト入力チャネルを使用します。 – Kaedys
私はそれを考えなかったことに同意します – reticentroot
なぜ10個のgoroutineを持っていて、できるだけ速く処理するのでしょうか? – JimB