我已经实现了一个黄金工人池如下,其中sem和工作是渠道。sem是跟踪当前活跃的工作人员(Goroutines)数量的一个渠道。work是将函数传递给活动员工执行的通道。超时将返回超时期间空闲的任何员工。
package main
import (
"time"
)
type Pool struct {
sem chan struct{}
work chan func()
timeout time.Duration
}
func NewPool(max, size, spawn int, timeout time.Duration) *Pool {
if spawn <= 0 {
panic("workpool spawn is <= 0")
}
if spawn > max {
panic("workpool spawn > max workers")
}
p := &Pool{
sem: make(chan struct{}, max),
work: make(chan func(), size),
timeout: timeout,
}
for i := 0; i < spawn; i++ {
p.sem <- struct{}{}
go p.worker(func() {})
}
return p
}
func (p *Pool) AddTask(task func()) {
select {
case p.work <- task:
return
case p.sem <- struct{}{}:
go p.worker(task)
return
}
}
func (p *Pool) worker(task func()) {
t := time.NewTimer(p.timeout)
defer func() {
t.Stop()
<- p.sem
}()
task()
for {
select {
case task := <- p.work:
t.Reset(p.timeout)
task()
case <- t.C:
return
}
}
}我是通过在for循环中打印i的值来测试的,方法是将它传递到以匿名函数包装的池中,如下所示:
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println("Hello, world!")
p := NewPool(3, 10, 1, time.Duration(5) * time.Second)
for i:=0; i<30; i++ {
p.AddTask(func () {
fmt.Print(i, " ")
})
}
time.Sleep(10 * time.Second)
fmt.Println("End")
}预期的输出应该是0到29之间的序列号,但是输出应该是
Hello, world!
12 12 12 12 12 12 12 12 12 12 12 12 13 25 25 25 25 25 25 25 25 25 25 25 26 25 30 30 30 30 End我不明白为什么输出是这样的。
发布于 2022-08-21 12:35:38
您的函数闭包都引用了相同的i值。这会创建一个争用条件,当函数被分派时,它们正在读取一个不断变化的值--因此您将看到不可预测的输出。
要确保闭包获得唯一值,请在循环中声明变量。要做到这一点,一个简单的技巧是通过阴影声明相同的变量名i := i。
for i:=0; i<30; i++ {
i:= i // <- add this
p.AddTask(func () {
fmt.Print(i, " ")
})
}https://go.dev/play/p/o0Nyx5A46tp
顺便说一句,这种技术在有效围棋文档中已经讨论过了,请参阅本节:
写起来似乎很奇怪 req := req 但这么做是合法的,也是惯用的。您将得到同名变量的新版本,故意在本地跟踪循环变量,但对每个goroutine都是唯一的。
https://stackoverflow.com/questions/73434264
复制相似问题