gqueue

动态大小的并发安全队列。

使用场景

该队列是并发安全的,常用于多goroutine数据通信的场景。

使用方式

  1. import "gitee.com/johng/gf/g/container/gqueue"

方法列表godoc.org/github.com/johng-cn/gf/g/container/gqueue

  1. type Queue
  2. func New(limit ...int) *Queue
  3. func (q *Queue) Close()
  4. func (q *Queue) PopBack() interface{}
  5. func (q *Queue) PopFront() interface{}
  6. func (q *Queue) PushBack(v interface{}) error
  7. func (q *Queue) PushFront(v interface{}) error
  8. func (q *Queue) Size() int

使用示例

  1. package main
  2. import (
  3. "fmt"
  4. "time"
  5. "gitee.com/johng/gf/g/os/gtime"
  6. "gitee.com/johng/gf/g/container/gqueue"
  7. )
  8. func main() {
  9. q := gqueue.New()
  10. // 数据生产者,每隔1秒往队列写数据
  11. gtime.SetInterval(time.Second, func() bool {
  12. v := gtime.Now().String()
  13. q.PushBack(v)
  14. fmt.Println("PushBack:", v)
  15. return true
  16. })
  17. // 3秒后关闭队列
  18. gtime.SetTimeout(3*time.Second, func() {
  19. q.Close()
  20. })
  21. // 消费者,不停读取队列数据并输出到终端
  22. for {
  23. if v := q.PopFront(); v != nil {
  24. fmt.Println("PopFront:", v)
  25. } else {
  26. break
  27. }
  28. }
  29. }

在该示例中,第3秒时关闭队列,这时程序立即退出,因此结果中只会打印2秒的数据。
执行后,输出结果为:

  1. PushBack: 2018-09-07 14:03:00
  2. PopFront: 2018-09-07 14:03:00
  3. PushBack: 2018-09-07 14:03:01
  4. PopFront: 2018-09-07 14:03:01

gqueue与glist

gqueue的底层基于list链表实现动态大小特性,但是gqueue的使用场景都是多goroutine下的并发安全通信场景。在队列满时存储(限制队列大小时),或者在队列空时读取数据会产生类似channel那样的阻塞效果。

glist是一个并发安全的链表,并可以允许在关闭并发安全特性的时和一个普通的list链表无异,在存储和读取数据时不会发生阻塞。

gqueue与channel

gqueue与原生channel的性能测试:

  1. john@johnstation:~/Workspace/Go/GOPATH/src/gitee.com/johng/gf/g/container/gqueue$ go test *.go -bench=".*"
  2. goos: linux
  3. goarch: amd64
  4. BenchmarkGqueueNew1000W-8 10000000 131 ns/op
  5. BenchmarkChannelNew1000W-8 200 5886021 ns/op
  6. BenchmarkGqueuePush-8 10000000 128 ns/op
  7. BenchmarkGqueuePushAndPop-8 20000000 111 ns/op
  8. BenchmarkChannelPushAndPop-8 50000000 39.3 ns/op
  9. PASS
  10. ok command-line-arguments 15.667s

可以看到原生的channel的读写性能是非常高的,但是创建的时候由于需要初始化内存,因此创建channel的时候效率非常非常低,并且受到队列大小的限制,写入的数据不能超过指定的队列大小。

gqueue使用起来比channel更加灵活,不仅创建效率高,不受队列大小限制(当然也可以指定大小),并且可以像操作双向链表那样执行队列头尾操作。从基准测试结果中也可以看得到,相比较channel,这些灵活性都是靠牺牲了一定的效率来实现的。不过相比较于channel,gqueuepush&pop效率也是相当得优异!