Golang 中基于时间的函数节流器

问题描述 投票:0回答:1

我的用例是:当用户停止在文本框中输入时,我将调用一个函数

process_input()
,我不能对每个字符都调用它,我只需要在输入完成后调用它。

所以思路是:使用辅助函数来节流

process_input()
,如果在一段时间内重复调用,之前的调用将被丢弃,一段时间后只会触发最后一次调用。

我想这样使用它:


func process_input() {
    fmt.Println(`process_input() called`)
}

func main() {
    fn := buffer.Timed(time.Second, process_input)
    fn() // shouldn't be called
    fn() // may be called or not
    time.Sleep(time.Second)
    fn() // shouldn't be called
    fn() // will be called

    time.Sleep(time.Hour) // block it from exiting
}

我的实现:

package buffer

import (
    "sync/atomic"
    "time"
)

func Timed(dur time.Duration, fn func()) func() {

    kill := make(chan bool)

    isScheduled := atomic.Bool{}

    return func() {
        if isScheduled.Load() { // if there is previous job scheduled
            kill <- true // kill the previous, sometimes this line causes deadlock.
        }

        // start the next
        isScheduled.Store(true)

        go func() {
            defer isScheduled.Store(false)

            select {
            case <-time.After(dur):
                go fn()
            case <-kill:
            }
        }()
    }
}

有时会死机崩溃

kill <- true
,如何解决?

go throttling
1个回答
0
投票

您在处理同步时犯了许多逻辑错误,但是您可以通过减少并发性并仅使用单个

time.Timer
来为您触发该函数来避免大多数复杂情况。

您的目标(至少如上面代码中所示)是在延迟后触发该函数,并在每次新调用时重置该延迟如果该函数尚未被调用。您可以使用单个

time.Timer
和互斥锁来保护该字段:https://go.dev/play/p/PaQRhKo44Bl

func NewDebouncer(dur time.Duration) func(fn func()) {
    d := &debouncer{
        dur: dur,
    }

    return func(fn func()) {
        d.reset(fn)
    }
}

type debouncer struct {
    mu    sync.Mutex
    dur   time.Duration
    delay *time.Timer
}

func (d *debouncer) reset(fn func()) {
    d.mu.Lock()
    defer d.mu.Unlock()

    if d.delay != nil {
        d.delay.Stop() {
    }

    d.delay = time.AfterFunc(d.dur, fn)
}
© www.soinside.com 2019 - 2024. All rights reserved.