package main
import (
"fmt"
"time"
"errors"
"sync"
"container/list"
)
type Itask interface {
Execute()
EchoID() int
}
type TestTask struct {
ID int
}
func (t *TestTask) Execute() {
fmt.Printf("TestTask| task id[%d] execute.\n", t.ID)
}
func (t *TestTask) EchoID() int {
return t.ID
}
type Queue struct {
sync.Mutex
in chan Itask
out chan Itask
stopCh chan struct {}
l *list.List
}
func NewQueue() *Queue {
q := &Queue {
in : make(chan Itask),
out : make(chan Itask),
stopCh : make(chan struct{}),
l : list.New(),
}
return q
}
// 启动异步消费协程
func (q *Queue) Start() {
go q.consume()
}
// 消费list中数据 将数据写入channel
func (q *Queue) consume() {
for {
if q.l.Len() == 0 {
fmt.Printf("Queue| q list len is 0.\n")
select {
case t := <-q.in:
q.l.PushBack(t)
case <-q.stopCh:
// graceful exit
if q.l.Len() != 0 {
q.out <- q.l.Front().Value.(Itask)
}
return
}
}
select {
case t := <-q.in:
fmt.Printf("Queue| consume task id[%d].\n", t.EchoID())
q.l.PushBack(t)
case q.out <-q.l.Front().Value.(Itask):
q.l.Remove(q.l.Front())
case <-q.stopCh:
taskNum := q.l.Len()
for i := 0; i < taskNum; i++ {
q.out <- q.l.Front().Value.(Itask)
q.l.Remove(q.l.Front())
}
return
}
}
}
// 将数据放入list
func (q *Queue) PutTask(t Itask) error {
fmt.Printf("Queue| put task id[%d].\n", t.EchoID())
// 为保证不阻塞 设置超时时间1s
timeout := time.After(time.Second * 1)
select {
case q.in <- t:
return nil
case <-timeout:
return errors.New("Queue| put task failed, timeout.")
}
}
// 关闭异步消费协程
func (q *Queue) Stop() {
close(q.stopCh)
}
// block读取channel中数据
func (q *Queue) GetTask() Itask {
return <-q.out
}
func main() {
var wg sync.WaitGroup
q := NewQueue()
q.Start()
wg.Add(2)
go func() {
for i := 0; i < 10; i++ {
t := new(TestTask)
t.ID = i
q.PutTask(t)
}
wg.Done()
}()
// 出于简单考虑
// 工程中生产者消费者需要提供stop功能
// 启动时先启动消费者 再启动生产者
// 关闭时应先stop生产者 后stop消费者
go func() {
for i := 0; i < 10; i++ {
t := q.GetTask()
t.Execute()
}
wg.Done()
}()
// 主协程阻塞
wg.Wait()
q.Stop()
return
}
golang不阻塞写协程
最新推荐文章于 2023-06-01 21:56:41 发布