发布于 2026-01-06 0 阅读
0

Golang模式 - 第3部分 DEV全球展示挑战赛,由Mux呈现:展示你的项目!

Golang模式 - 第三部分

由 Mux 赞助的 DEV 全球展示挑战赛:展示你的项目!

在这篇文章中,我将展示一个使用 golang channels 的很酷的模式:channels 的 channels

通道是 Go 语言中非常灵活的工具。学会正确使用通道,就能充分发挥 Go 的强大功能。
但本文并非旨在解释什么是通道或何时需要使用通道。
如果您想了解更多关于通道的信息,我建议您阅读:

玩具问题

假设你需要定期运行某个任务,例如每秒一次。
但有时,你需要尽快运行该任务,这意味着你不能等待任务触发。
一个简单的例子是文件同步工具,它可以同步不同主机之间的文件。你希望每隔x
秒 同步一次文件,但有时你需要按需同步;也就是在预定义的计划之外强制执行同步。

通道的通道

这是解决问题的一种方法,它利用了多层渠道。

const (
    running    int32 = 1
    notRunning int32 = 0
)

type Worker struct {
    syncStrategy      func() error
    period            time.Duration
    currentStatus     *int32
    periodicSyncChan  chan int
    outOfBandSyncChan chan chan error
    exitChan          chan int
}

func NewSyncWorker(syncStrategy func() error, period time.Duration) *Worker {
    periodicSyncChan := make(chan int)
    outOfBandSyncChan := make(chan chan error)
    exitChan := make(chan int)
    var currentStatus = notRunning

    return &Worker{
        syncStrategy:      syncStrategy,
        period:            period,
        currentStatus:     &currentStatus,
        periodicSyncChan:  periodicSyncChan,
        outOfBandSyncChan: outOfBandSyncChan,
        exitChan:          exitChan,
    }
}

func (s *Worker) Sync() error {
    if atomic.CompareAndSwapInt32(s.currentStatus, notRunning, running) {
        return s.doSync()
    } else {
        return errors.New("Sync is already running")
    }
}

func (s *Worker) doSync() error {
    if e := s.syncStrategy(); e != nil {
        return e
    }
    time.AfterFunc(s.period, func() {
        s.periodicSyncChan <- 1
    })

    for true {
        select {
        case <-s.periodicSyncChan:
            {
                s.syncStrategy()
                time.AfterFunc(s.period, func() {
                    s.periodicSyncChan <- 1
                })
            }
        case responseChannel := <-s.outOfBandSyncChan:
            responseChannel <- s.syncStrategy()
        case <-s.exitChan:
            {
                atomic.CompareAndSwapInt32(s.currentStatus, running, notRunning)
                return nil
            }
        }
    }
    return nil
}

func (s *Worker) OutOfBandSync() error {
    responseChannel := make(chan error)
    s.outOfBandSyncChan <- responseChannel
    return <-responseChannel
}

func (s *Worker) Stop() {
    s.exitChan <- 1
}

func (s *Worker) IsRunning() bool {
    return *s.currentStatus == running
}
Enter fullscreen mode Exit fullscreen mode

outOfBandSyncChan是一个通道的通道。 当客户端请求OutOfBandSync时,该函数会创建一个 responseChannel。responseChan添加到outOfBandSyncChan,并在操作完成后用于返回一个值(在本例中为 nil 或错误)

换句话说,我们是:

  • 创建一个通道,用于将响应发送回客户端。
  • 将新创建的通道传输到负责通过另一个通道进行同步的代码。

更重要的是,我们没有使用锁。不同的 goroutine 之间通过发送消息进行通信,这种方式非常优雅。

更多 Go 语言模式

如果你喜欢这篇文章,请查看:

你可以在TwitterGitHub上找到我。

玩得开心!

文章来源:https://dev.to/napicella/golang-patterns-part-3-apo