Go中的批量处理
批处理通常用于将大量的工作分割成较小的块状,以达到最佳的处理效果。在这里,我们来看看Go中批量处理的两种方法。
-
Jan. 05, 22 - Web Dev Zone -教程
喜欢 (3)
评论
保存
鸣叫
7.46K浏览次数
加入DZone社区,获得完整的会员体验。
批量处理是开发人员经常遇到的情况,基本上是把大量的工作分成小块,以达到最佳的处理效果。看起来很简单,而且确实如此。假设我们有一个长长的项目清单,我们想以某种方式进行处理。其中预先定义的数量可以被并发处理。我可以看到有两种不同的方式在Go中进行处理。
第一种方法是使用普通的分片。这是大多数开发者在其职业生涯中的某个阶段可能都做过的事情。让我们来看看这个简单的例子。
Go
func main() {
data := make([]int, 0, 100)
for n := 0; n < 100; n++ {
data = append(data, n)
}
process(data)
}
func processBatch(list []int) {
var wg sync.WaitGroup
for _, i := range list {
x := i
wg.Add(1)
go func() {
defer wg.Done()
// do more complex things here
fmt.Println(x)
}()
}
wg.Wait()
}
const batchSize = 10
func process(data []int) {
for start, end := 0, 0; start <= len(data)-1; start = end {
end = start + batchSize
if end > len(data) {
end = len(data)
}
batch := data[start:end]
processBatch(batch)
}
fmt.Println("done processing all data")
}
要处理的数据是一个普通的整数列表。为了简单起见,我们只想打印所有的数据,最多同时打印10个。为了达到这个目的,我们在列表上循环,把它分成batchSize = 10 ,然后连续处理每一批。简洁明了,而且达到了我们的要求。
第二种方法是使用一个缓冲通道,类似于这篇关于并发的文章中所描述的那样。让我们先看一下代码。
去
func main() {
data := make([]int, 0, 100)
for n := 0; n < 100; n++ {
data = append(data, n)
}
batch(data)
}
const batchSize = 10
func batch(data []int) {
ch := make(chan struct{}, batchSize)
var wg sync.WaitGroup
for _, i := range data {
wg.Add(1)
ch <- struct{}{}
x := i
go func() {
defer wg.Done()
// do more complex things here
fmt.Println(x)
<-ch
}()
}
wg.Wait()
fmt.Println("done processing all data")
}
这个例子使用了一个大小为10的缓冲通道。当每个项目准备好被处理时,它试图向通道发送。发送在10个项目后被阻止。一旦处理完毕,它就从通道中读出,从而从缓冲区中释放出来。使用struct{}{} ,可以为我们节省一些空间,因为无论什么东西被发送到通道都不会被使用。
正如帖子的作者所指出的,在这里我们利用了缓冲通道的特性来限制并发性。有人可能会说,这不是真正的批处理,而是有阈值的并发处理。我完全同意。不管怎么说,它能完成工作,而且代码也更简单。
它比分片好吗?可能没有。至于速度,我对两个程序的执行进行了计时,它们运行得很接近。这些例子太简单了,在运行时间上看不出任何明显的区别。一般来说,通道比分片更慢、更昂贵。由于在goroutines之间没有任何有意义的数据被传递,这可能是一种浪费。那么,我为什么要这样做呢?好吧,我喜欢简单的代码,但这可能并不是一个足够的理由。如果每个批次的串行处理的成本超过了使用通道的成本,这可能是值得考虑的。
主题。
编程、 Go、切片、 缓冲通道、 批处理
DZone贡献者所表达的观点属于他们自己。
DZone上的热门话题
评论