生产者 - 消费者模型(Producer - Consumer Model)§

生产者 - 消费者模型是一种经典的并发编程模型。它主要涉及两个角色:生产者(Producer)和消费者(Consumer),以及一个用于存储数据的共享缓冲区(Buffer)。生产者的主要职责是生成数据,并将数据放入缓冲区;消费者则从缓冲区中取出数据进行处理。

package main

import (
    "context"
    "fmt"
    "time"

    "github.com/gin-gonic/gin"
)

// TokenProducer结构体用于管理生产者相关的状态和操作
type TokenProducer struct {
    last     time.Time
    id       int
    tokenCh  chan int
    stopChan chan struct{} // 用于接收停止信号的通道
}

// NewTokenProducer创建一个新的TokenProducer实例
func NewTokenProducer() *TokenProducer {
    return &TokenProducer{
        last:     time.Now(),
        id:       1,
        tokenCh:  make(chan int, 10),
        stopChan: make(chan struct{}),
    }
}

// Produce方法实现生产者的核心逻辑,按一定时间间隔向通道发送令牌
func (p *TokenProducer) Produce() {
    for {
        select {
        case <-p.stopChan:
            // 收到停止信号,关闭tokenCh通道并退出循环
            close(p.tokenCh)
            return
        default:
            if time.Since(p.last).Seconds() >= 1 {
                p.tokenCh <- p.id
                p.id++
                p.last = time.Now()
            }
        }
    }
}

// Stop方法用于向生产者发送停止信号
func (p *TokenProducer) Stop() {
    p.stopChan <- struct{}{}
}

// TokenConsumer函数作为消费者,从通道获取令牌并处理
func TokenConsumer(ctx *gin.Context, tokenCh chan int) {
    select {
    case id, ok := <-tokenCh:
        if ok {
            ctx.JSON(200, gin.H{
                "token": id,
            })
        } else {
            ctx.JSON(200, gin.H{
                "message": "Token channel is closed",
            })
        }
    default:
        ctx.JSON(200, gin.H{
            "message": "No token available",
        })
    }
}

func main() {
    r := gin.Default()
    producer := NewTokenProducer()

    // 启动生产者协程
    go producer.Produce()

    // 定义一个路由,用于获取令牌
    r.GET("/", func(ctx *gin.Context) {
        TokenConsumer(ctx, producer.tokenCh)
    })

    // 模拟程序运行一段时间后停止,这里设置为运行10秒后停止
    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    defer cancel()

    go func() {
        <-ctx.Done()
        producer.Stop()
        fmt.Println("Producer stopped")
    }()

    r.Run()
}