如何动态决定处理任务的 goroutines 数量
How to dynamically decide on number of goroutines working on a task
我写了一个伪代码来演示目的。
代码中有2个通道和3个协程。
1 goroutine 正在根据它们是否可以被 100 整除而没有余数来生成数字:
如果数字能被100整除,则推送到第一个频道
否则推到第二通道
2 个 goroutines 是这些通道的消费者:
1个goroutine负责消费数字1...99 - 101...199等
其他goroutine负责100,200,300等
现在很明显,一个 goroutine 比另一个 goroutine 多 99 倍的工作要做。
这在 Go 中是如何处理的?如果一个 goroutine 比其他 goroutine 工作得更多,这个 goroutine 是否有更多 CPU 时间?
还是我应该处理这种情况,例如为资源消耗量更大的频道创建 99 个 goroutine? (为了争论,这些工作被认为是相同的)
func main() {
ch1 := make(chan int)
ch2 := make(chan int)
go generator(ch1, ch2)
go handler(ch1)
go handler2(ch2)
time.Sleep(1*time.Second)
}
func generator(chan1, chan2 chan int){
for i:=0 ; ; i++{
if i%100 == 0{
chan1 <- i
}else{
chan2 <- i
}
}
}
func handler(number chan int){
for _ = range number{
num := <- number
fmt.Println("Number divided by 100 is 0. ", num)
}
}
func handler2(number chan int){
for _ = range number{
num := <- number
fmt.Println("Number divided by 100 is not 0. ", num)
}
}
goroutine 获得多少 CPU 资源取决于很多因素。
我们一般可以说的是,仅处理可被 100 整除的数字的 goroutine 很可能会比另一个 goroutine 等待更多。你不应该担心这个,等待通道上的元素不需要 CPU 资源,所以如果你有 "enough" 其他有工作要做的 goroutines,他们可以利用你的 CPU.
你的例子很简单,原因很明显,但在现实生活中,将你的任务抽象为一般任务(例如,处理任何数字都可能是一项任务)、创建和使用一般工作池会更有利可图,并将所有要执行的任务发送到池中。这样,无论池中有多少 goroutine,如果有工作要做并且有空闲(等待)goroutine,它将承担任务,尽可能利用您的 CPU 资源。作业处理器(执行者)应该知道如何处理 100
或 101
.
的数字
有关如何实现此类 goroutine 池的示例,请参阅
如果 ch2
上没有任何内容可供读取,handler2
将不执行任何操作。同时,handler1
忙于处理它从 ch1
读取的内容。这会消耗更多 CPU 时间。
有趣的是从启动三个 Goroutine 到数据在通道上可用需要多长时间。 generator
可能在 handler1
和 handler2
准备好从通道读取之前完成所有工作。在这种情况下,handler2
将很快完成其工作,而 handler1
仍有工作要做。
您当然可以创建更多 handler1
的 Goroutine,它们将以一种循环方式处理数据。根据工作的性质,这可能会提高或降低整体性能。
顺便说一句,generator
应该 close
ch1
和 ch2
都写好了。
我写了一个伪代码来演示目的。
代码中有2个通道和3个协程。
1 goroutine 正在根据它们是否可以被 100 整除而没有余数来生成数字:
如果数字能被100整除,则推送到第一个频道
否则推到第二通道
2 个 goroutines 是这些通道的消费者:
1个goroutine负责消费数字1...99 - 101...199等
其他goroutine负责100,200,300等
现在很明显,一个 goroutine 比另一个 goroutine 多 99 倍的工作要做。 这在 Go 中是如何处理的?如果一个 goroutine 比其他 goroutine 工作得更多,这个 goroutine 是否有更多 CPU 时间? 还是我应该处理这种情况,例如为资源消耗量更大的频道创建 99 个 goroutine? (为了争论,这些工作被认为是相同的)
func main() {
ch1 := make(chan int)
ch2 := make(chan int)
go generator(ch1, ch2)
go handler(ch1)
go handler2(ch2)
time.Sleep(1*time.Second)
}
func generator(chan1, chan2 chan int){
for i:=0 ; ; i++{
if i%100 == 0{
chan1 <- i
}else{
chan2 <- i
}
}
}
func handler(number chan int){
for _ = range number{
num := <- number
fmt.Println("Number divided by 100 is 0. ", num)
}
}
func handler2(number chan int){
for _ = range number{
num := <- number
fmt.Println("Number divided by 100 is not 0. ", num)
}
}
goroutine 获得多少 CPU 资源取决于很多因素。
我们一般可以说的是,仅处理可被 100 整除的数字的 goroutine 很可能会比另一个 goroutine 等待更多。你不应该担心这个,等待通道上的元素不需要 CPU 资源,所以如果你有 "enough" 其他有工作要做的 goroutines,他们可以利用你的 CPU.
你的例子很简单,原因很明显,但在现实生活中,将你的任务抽象为一般任务(例如,处理任何数字都可能是一项任务)、创建和使用一般工作池会更有利可图,并将所有要执行的任务发送到池中。这样,无论池中有多少 goroutine,如果有工作要做并且有空闲(等待)goroutine,它将承担任务,尽可能利用您的 CPU 资源。作业处理器(执行者)应该知道如何处理 100
或 101
.
有关如何实现此类 goroutine 池的示例,请参阅
如果 ch2
上没有任何内容可供读取,handler2
将不执行任何操作。同时,handler1
忙于处理它从 ch1
读取的内容。这会消耗更多 CPU 时间。
有趣的是从启动三个 Goroutine 到数据在通道上可用需要多长时间。 generator
可能在 handler1
和 handler2
准备好从通道读取之前完成所有工作。在这种情况下,handler2
将很快完成其工作,而 handler1
仍有工作要做。
您当然可以创建更多 handler1
的 Goroutine,它们将以一种循环方式处理数据。根据工作的性质,这可能会提高或降低整体性能。
顺便说一句,generator
应该 close
ch1
和 ch2
都写好了。