这是一个单一生产者,多个消费者的模型。对之前的代码做了改进。
目标:
- 包装成包的形式。包的名子叫pc, producer/consumer的简写。
- 使用者只需要写自己实际的生产逻辑和消费逻辑即可。
1. 实现
package pc
import (
"sync"
)
type Task interface {
}
type AbstructPC struct {
ConsumerNum int
ChanLen int
Tasks chan Task
Consumer func(Task)
Producer func(chan Task)
wg sync.WaitGroup
}
func (a *AbstructPC) Init(cl int, cn int, p func(chan Task), c func(Task)) {
a.ChanLen = cl
a.ConsumerNum = cn
a.Tasks = make(chan Task, a.ConsumerNum)
a.Producer = p
a.Consumer = c
}
func (a *AbstructPC) producerDispatch() {
defer close(a.Tasks)
a.Producer(a.Tasks)
}
func (a *AbstructPC) consumerDispatch() {
for i := 0; i < a.ConsumerNum; i++ {
a.wg.Add(1)
go func() {
defer a.wg.Done()
for task := range a.Tasks {
a.Consumer(task)
}
}()
}
a.wg.Wait()
}
func (a *AbstructPC) Run() {
go a.producerDispatch()
a.consumerDispatch()
}
思路:
想实现类似抽象类的功能,于是在AbstructPC中预置了两个空函数
Consumer func(Task)
Producer func(chan Task)
使用者需要实现这两个方法,并在初始化时(Init)将其以参数形式传入。如果使用者未实现这两个方法,运行时会报错。
这个实现也许这不是最佳的方式,但对于初学者的我,目前只能想到这样的方式了>_<|||
2. 使用
2.1 安装
代码放到了github, 所以可以这样安装:
go get -u github.com/qmhball/gopc
当然也可以直接将上面代码放到$GOPATH/gopc/下,注意不同的方式,import时的写法会有点区别。
2.2 需要自定的内容
- 实际task的数据结构
- 实际生产函数
func Producer(Tasks chan pc.Task) {}
- 实际消费函数
func Consumer(task pc.Task) {}
- 消费者个数,通道长度
2.3 示例
该示例自定义实际数据格式
type Person struct {}
生产者生产了10条数据,将其json encode后放入通道,消费者取出后json decode输出。main中的几行代码是pc的调用demo。
package main
import (
"fmt"
"pc"
"strconv"
)
type Person struct {
Name string
Age int
}
//实际生产逻辑
func Producer(Tasks chan pc.Task) {
p := Person{}
for i := 0; i < 10; i++ {
p.Name = "Person" + strconv.Itoa(i)
p.Age = i + 10
//Task是空接口,Person默认实现了它,自然可以直接放入Task类型的通道
Tasks <- p
}
}
//实际消费数据逻辑
func Consumer(task pc.Task) {
//通过断言将task转为Person类型,不能直接使用task.Name, task,Age
p := task.(Person)
fmt.Printf("consum task:%s, %d\n", p.Name, p.Age)
}
func main() {
var mypc pc.AbstructPC
chanLen := 50
consumerNum := 20
mypc.Init(chanLen, consumerNum, Producer, Consumer)
mypc.Run()
}
使用时,唯一需要注意的是Consumer中取到task数据后,要使用断言将其转为你真实的数据类型。