dongya3627 2017-07-20 07:46
浏览 83
已采纳

在Go中返回锁定互斥锁的好方法

Following problem: I have a function that only should allow one caller to execute. If someone tries to call the function and it is already busy the second caller should immediatly return with an error.

I tried the following:

1. Use a mutex

Would be pretty easy. But the problem is, you cannot check if a mutex is locked. You can only block on it. Therefore it does not work

2. Wait on a channel

var canExec = make(chan bool, 1)

func init() {
    canExec <- true
}

func onlyOne() error {
    select {
    case <-canExec:
    default:
        return errors.New("already busy")
    }

    defer func() {
        fmt.Println("done")
        canExec <- true
    }()

    // do stuff

}

What I don't like here:

  • looks really messi
  • if easy to mistakenly block on the channel / mistakenly write to the channel

3. Mixture of mutex and shared state

var open = true

var myMutex *sync.Mutex

func canExec() bool {
    myMutex.Lock()
    defer myMutex.Unlock()

    if open {
        open = false
        return true
    }

    return false
}

func endExec() {
    myMutex.Lock()
    defer myMutex.Unlock()

    open = true
}

func onlyOne() error {
    if !canExec() {
        return errors.New("busy")
    }
    defer endExec()

    // do stuff

    return nil
}

I don't like this either. Using a shard variable with mutex is not that nice.

Any other idea?

  • 写回答

5条回答 默认 最新

  • dongmiyi8220 2017-07-20 10:06
    关注

    You can use a semaphore for this (go get golang.org/x/sync/semaphore)

    package main
    
    import (
        "errors"
        "fmt"
        "sync"
        "time"
    
        "golang.org/x/sync/semaphore"
    )
    
    var sem = semaphore.NewWeighted(1)
    
    func main() {
        var wg sync.WaitGroup
        for i := 0; i < 10; i++ {
            wg.Add(1)
            go func() {
                defer wg.Done()
                if err := onlyOne(); err != nil {
                    fmt.Println(err)
                }
            }()
            time.Sleep(time.Second)
        }
        wg.Wait()
    }
    
    func onlyOne() error {
        if !sem.TryAcquire(1) {
            return errors.New("busy")
        }
        defer sem.Release(1)
        fmt.Println("working")
        time.Sleep(5 * time.Second)
        return nil
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(4条)

报告相同问题?

悬赏问题

  • ¥15 #MATLAB仿真#车辆换道路径规划
  • ¥15 java 操作 elasticsearch 8.1 实现 索引的重建
  • ¥15 数据可视化Python
  • ¥15 要给毕业设计添加扫码登录的功能!!有偿
  • ¥15 kafka 分区副本增加会导致消息丢失或者不可用吗?
  • ¥15 微信公众号自制会员卡没有收款渠道啊
  • ¥100 Jenkins自动化部署—悬赏100元
  • ¥15 关于#python#的问题:求帮写python代码
  • ¥20 MATLAB画图图形出现上下震荡的线条
  • ¥15 关于#windows#的问题:怎么用WIN 11系统的电脑 克隆WIN NT3.51-4.0系统的硬盘