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条)

报告相同问题?

悬赏问题

  • ¥30 自适应 LMS 算法实现 FIR 最佳维纳滤波器matlab方案
  • ¥15 lingo18勾选global solver求解使用的算法
  • ¥15 全部备份安卓app数据包括密码,可以复制到另一手机上运行
  • ¥15 Python3.5 相关代码写作
  • ¥20 测距传感器数据手册i2c
  • ¥15 RPA正常跑,cmd输入cookies跑不出来
  • ¥15 求帮我调试一下freefem代码
  • ¥15 matlab代码解决,怎么运行
  • ¥15 R语言Rstudio突然无法启动
  • ¥15 关于#matlab#的问题:提取2个图像的变量作为另外一个图像像元的移动量,计算新的位置创建新的图像并提取第二个图像的变量到新的图像