如何在频道中同步待处理结果?

时间:2014-07-13 13:45:47

标签: go locking sync

我有一个工作池,它提供了一个同步接口来提取结果:

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频道的长度。

如果缓冲区为空,什么是不返回任何内容的惯用方法?

2 个答案:

答案 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