首页 > 解决方案 > 如何运行 goroutine 的单个实例

问题描述

我有一个将运行多次的 goroutine。但它一次只能运行一个(单个实例)。确保某个 goroutine 一次只能运行一个的正确/惯用方法是什么?

这是我设计的示例代码来说明这一点:

func main() {
    // Contrived example!!!!!!
    // theCaller() may be run at multiple, unpredictable times
    // theJob() must only be run one at a time
    go theCaller()
    go theCaller()
    go theCaller()
}

func theCaller() {
    if !jobIsRunning { // race condition here!
        jobIsRunning = true
        go theJob()
    }
}

var jobIsRunning bool

// Can run multiple times, but only one at a time
func theJob() {
    defer jobDone()
    do_something()
}

func jobDone() {
    jobIsRunning = false
}

标签: gothread-safetygoroutine

解决方案


根据 OP 的问题和其他评论,目标似乎是当且仅当工作尚未运行时才开始新工作。

使用受sync.Mutex保护的布尔变量来记录作业的运行状态。开始作业时将变量设置为 true,作业完成时设置为 false。测试此变量以确定是否应启动作业。

var (
    jobIsRunning   bool
    JobIsrunningMu sync.Mutex
)

func maybeStartJob() {
    JobIsrunningMu.Lock()
    start := !jobIsRunning
    jobIsRunning = true
    JobIsrunningMu.Unlock()
    if start {
        go func() {
            theJob()
            JobIsrunningMu.Lock()
            jobIsRunning = false
            JobIsrunningMu.Unlock()
        }()
    }
}

func main() {
    maybeStartJob()
    maybeStartJob()
    maybeStartJob()
}

也可以使用较低级别的同步/原子包,并且可能比使用互斥锁具有更好的性能。

var jobIsRunning uint32

func maybeStartJob() {
    if atomic.CompareAndSwapUint32(&jobIsRunning, 0, 1) {
        go func() {
            theJob()
            atomic.StoreUint32(&jobIsRunning, 0)
        }()
    }
}

sync/atomic 包文档警告说,包中的函数需要非常小心才能正确使用,并且大多数应用程序应该使用 sync 包。


推荐阅读