我有一些30&#39000字符串。如何将这个切片分成10个goroutine,从切片中取出3000个字符串,从中提取一些数据并推入新切片?
所以,最后,我将有10个切片,每个切片有3000个处理结果。处理这个问题的模式是什么?
我看过this article,但不确定这些模式中哪些适用于我的情况。
答案 0 :(得分:2)
使用频道,从片中读取元素,使用扇出来分发负载并传递消息。然后,处理goroutines中的字符串并在单个goroutine中收集结果(扇入)以避免互斥。
您可能需要设置Max并发并发goroutine的数量。
请记住,切片在写入时不是线程安全的。
有用信息:
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 :(得分:0)
我同意@JimB的原因是为什么限制go例程。然而,因为那是你的询问,我可能会这样。如果你真的想让每个gorountine做3000项,那么创建一个2d切片可能会更容易。 [[3000项],[3000项],..]然后在该2d数组中每个索引有1个常规进程。否则以下将gorountines限制为10。 方法1 包主要
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)
}
}
METHOD2 init函数正在创建一个2d数组,分成10个数组,每个数组包含3000个元素..如果你解析数据,那么下面的逻辑需要很少的修改才能工作
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++
}
}
答案 2 :(得分:0)
我开发了一个库 parapipe 来解决这样的任务。只需将您的切片切成 10 个部分并将它们发送到流水线,流水线将同时处理它们:
import "github.com/nazar256/parapipe"
//...
var longStringSlice []string
// ...
pipeline := parapipe.NewPipeline(10).
Pipe(func(msg interface{}) interface{} {
slicePart := msg.([]string)
// process here to result
return result
})
// chop the slice and stream parts
chopSize := int(math.Ceil(float64(len(longStringSlice)) / 10))
for i:=0;i<10;i++ {
firstIdx := i * chopSize
lastIdx := (i+1) * chopSize
if lastIdx > len(longStringSlice) {
lastIdx = len(longStringSlice)
}
pipeline.In() <- longStringSlice[firstIdx:lastIdx]
}
答案 3 :(得分:0)
在不同的 goroutine 中处理不同的内存地址没有问题。如果您的读写操作不会掩盖块,那么您可以简单地使用 massif 中的范围。 https://play.golang.org/p/MU5njoNmIf7