I'm passing a big object via channel between goroutines, one goroutine for publishing, many goroutines subscribe and wait for messages, it's so frequent that I wanna use sync.Pool
to reduce the number of allocations, example code like this:
package main
import (
"log"
"sync"
"time"
)
var pool *sync.Pool
type object struct {
info string
// other stuff
}
func initPool() {
pool = &sync.Pool{
New: func() interface{} {
return new(object)
},
}
}
var (
lock sync.RWMutex
registry = make(map[string][]chan interface{})
)
func sub(topic string) chan interface{} {
ch := make(chan interface{}, 10)
lock.Lock()
registry[topic] = append(registry[topic], ch)
lock.Unlock()
return ch
}
// publish goroutine
func pub() {
ticker := time.NewTicker(time.Second)
o := pool.Get().(*object)
o.info = "new"
// do something
for _ = range ticker.C {
lock.RLock()
for topic, chs := range registry {
if o.info != "new" {
log.Printf("error")
}
if topic == "hello" {
for _, ch := range chs {
select {
case ch <- o:
default:
}
}
}
}
lock.RUnlock()
}
}
func run(topic string) {
ch := sub(topic)
for {
select {
case o := <-ch:
switch o := o.(type) {
case *object:
if o.info != "new" {
log.Printf("error")
}
// do something
time.Sleep(time.Second)
o.info = "used"
pool.Put(o)
}
}
}
}
func main() {
initPool()
for i := 0; i <= 100; i++ {
go run("hello")
}
pub()
}
The question is the object borrowed from pool can be accessed by multiple goroutines, so if one goroutine puts it back to the pool after using, it may be modified by other goroutines. I don't know when the object had processed by all goroutines, so that I can put it back to pool.
One should call pool.Put()
after he is done with the pooled object, ie it is considered to be "garbage".
If you would put the o
back into the pool in the pub
then the next iteration of the loop could get the same object and assign new values to it's properties before it has been proccessed by the run
. So right place to put your object back into pool is in run
, after you have proccessed it.