【问题标题】:How to make a restartable, thread-safe AfterFunc() call如何进行可重新启动、线程安全的 AfterFunc() 调用
【发布时间】:2020-08-11 11:15:48
【问题描述】:

如何以线程安全的方式进行延迟的、可重新启动的函数调用?这是一个非常常见的用例,例如,如果您想对某些更改做出反应,但频率不会超过 X 毫秒一次。

我能想到的代码是:

var t *time.Timer
func restartableDelayed()  {
    if t != nil {
        t.Stop()
    }
    t = time.AfterFunc(time.Second, func() {
        t = nil
        // ...
        // Do the stuff here
        // ...
    })
}

这里明显的问题是 t = nil 赋值是从另一个 goroutine 调用的,而不是初始赋值,这会受到竞争。

如何以适当的并发安全方式完成此操作?在这里我唯一能想到的是涉及一个互斥锁来同步对t的访问,但这感觉有点麻烦。还有更简洁的选项吗?

【问题讨论】:

  • 如果之前已经调用过,但是超时没有过期,又发出了新的调用,你想怎么办?丢弃它?超时后排队吗?
  • 是的,丢弃。因此t.Stop() 调用
  • 您可能会发现 golang.org/x/time/rate 和 golang.org/x/sync/singleflight 很有趣。

标签: go concurrency


【解决方案1】:

我认为这里使用互斥锁是合理的,想不出更简单的解决方案。

你只需要添加几行就可以了:

var (
    mu sync.Mutex
    t  *time.Timer
)

func restartableDelayed() {
    mu.Lock()
    defer mu.Unlock()

    if t != nil {
        t.Stop()
    }
    t = time.AfterFunc(time.Second, func() {
        mu.Lock()
        t = nil
        mu.Unlock()

        // Do the stuff here
    })
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-10-20
    • 1970-01-01
    • 2014-11-09
    • 1970-01-01
    • 2012-03-25
    相关资源
    最近更新 更多