gqueue

动态大小的并发安全队列。同时,gqueue也支持固定队列大小,固定队列大小时队列效率和标准库的channel无异。

使用场景

该队列是并发安全的,常用于多goroutine数据通信且支持动态队列大小的场景。

使用方式

  1. import "github.com/gogf/gf/g/container/gqueue"

接口文档

https://godoc.org/github.com/gogf/gf/g/container/gqueue

使用示例1,基本使用

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

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

  1. Push: 2018-09-07 14:03:00
  2. Pop: 2018-09-07 14:03:00
  3. Push: 2018-09-07 14:03:01
  4. Pop: 2018-09-07 14:03:01

使用示例2,结合select语法使用

使用队列对象公开的Queue.C属性,结合selectIO复用语法实现对队列的读取。

  1. package main
  2. import (
  3. "fmt"
  4. "github.com/gogf/gf/g/container/gqueue"
  5. "github.com/gogf/gf/g/os/gtime"
  6. "github.com/gogf/gf/g/os/gtimer"
  7. "time"
  8. )
  9. func main() {
  10. queue := gqueue.New()
  11. // 数据生产者,每隔1秒往队列写数据
  12. gtimer.SetInterval(time.Second, func() {
  13. queue.Push(gtime.Now().String())
  14. })
  15. // 消费者,不停读取队列数据并输出到终端
  16. for {
  17. select {
  18. case v := <-queue.C:
  19. if v != nil {
  20. fmt.Println(v)
  21. } else {
  22. return
  23. }
  24. }
  25. }
  26. }

执行后,输出结果为:

  1. 2019-01-23 20:42:01
  2. 2019-01-23 20:42:02
  3. 2019-01-23 20:42:03
  4. 2019-01-23 20:42:04
  5. 2019-01-23 20:42:05
  6. 2019-01-23 20:42:06
  7. ...

gqueue与glist

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

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

gqueue与channel

gqueue与标准库channel的性能测试,其中每一次基准测试的b.N值均为20000000,以保证动态队列存取一致防止deadlock:

  1. goos: darwin
  2. goarch: amd64
  3. pkg: github.com/gogf/gf/g/container/gqueue
  4. Benchmark_Gqueue_StaticPushAndPop-4 20000000 84.2 ns/op
  5. Benchmark_Gqueue_DynamicPush-4 20000000 164 ns/op
  6. Benchmark_Gqueue_DynamicPop-4 20000000 121 ns/op
  7. Benchmark_Channel_PushAndPop-4 20000000 70.0 ns/op
  8. PASS

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

gqueue使用起来比channel更加灵活,不仅创建效率高(动态分配内存),不受队列大小限制(也可限定大小)。