我有一个工作池,它提供了一个同步接口来提取结果:
func (p *Pool) Get() *Result {
for {
select {
// if there are results in channel return them
case r := <-p.results:
return r
// else check if there is any work pending we must wait for
// if not return nil to indicate that all work was done
default:
if p.active < 1 {
return nil
}
}
}
}
想法是,如果所有工作都完成,Get将返回下一个工作结果或nil
。
现在这个实现的问题是我需要手动跟踪p.active
计数器的所有活动工作。这感觉有点不对,理论上信息已经在p.results
频道的长度。
如果缓冲区为空,什么是不返回任何内容的惯用方法?
答案 0 :(得分:1)
可悲的是,没有len(chan)
,如果你不知道工人的数量,你的方法就会很好。
但是你需要为计数器进行某种同步,这是一种非常简单的方法:
type Result struct {
I int
}
type Pool struct {
res chan *Result
c int32
}
func New() *Pool {
return &Pool{
res: make(chan *Result),
}
}
func (p *Pool) Put(r *Result) {
atomic.AddInt32(&p.c, 1)
time.Sleep(time.Duration(100+r.I%1000) * time.Microsecond)
p.res <- r
}
func (p *Pool) Get() (r *Result) {
for {
select {
case r = <-p.res:
atomic.AddInt32(&p.c, -1)
return
default:
if atomic.LoadInt32(&p.c) == 0 {
return
}
}
}
}
func main() {
runtime.GOMAXPROCS(8)
p := New()
for i := 0; i < 50; i++ {
go p.Put(&Result{i})
}
time.Sleep(10 * time.Microsecond)
for {
r := p.Get()
if r == nil {
return
}
fmt.Println("r.I", r.I)
}
}
//修改
为了完整起见,这是使用WaitGroup的另一个例子,但是这又是一个过度杀手,因为内部WG无论如何都使用原子计数器。
type Pool struct {
res chan *Result
wg sync.WaitGroup
}
func New(n int) (p *Pool) {
p = &Pool{
res: make(chan *Result, n),
}
p.wg.Add(n)
go func() {
p.wg.Wait()
close(p.res)
}()
return
}
func (p *Pool) Get() *Result {
for {
r, ok := <-p.res
if !ok {
return nil
}
p.wg.Done()
return r
}
}
//func Put is the same as above and the test code is the same.
答案 1 :(得分:0)
您可以使用WaitGroup
和阻挡它的gorouotine,并在完成时更改一些标记。
所以说你有一个waitGroup,并且你排队的每个项目都会在其上调用wg.Add(1)
。每次您在频道上收到,或每次工作人员完成时,都可以致电wg.Done()
以减少其计数器。
然后你有一个goroutine等待evertyhing完成,只是设置一个标志:
go func() {
wg.Wait()
p.done = true //do this thread safe of course
}
在默认情况下,您只需检查完成标志
default:
if p.done {
return nil
}
有关详细信息,请参阅WaitGroup
上的文档。这个例子有点类似于你的情况。 http://golang.org/pkg/sync/#WaitGroup.Wait