• gqueue
    • 使用示例1,基本使用
    • 使用示例2,结合select语法使用
    • gqueue与glist
    • gqueue与channel

    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更加灵活,不仅创建效率高(动态分配内存),不受队列大小限制(也可限定大小)。