Golang - 按值锁定

too*_*op4 8 go

我正在编写一个接受 tableName 值和 updEpoch 值的 golang api,即:

curl -F "tableName=abc" -F "updEpoch=123" myhost:8080/singleroute
curl -F "tableName=abc" -F "updEpoch=456" myhost:8080/singleroute
curl -F "tableName=def" -F "updEpoch=123" myhost:8080/singleroute
curl -F "tableName=def" -F "updEpoch=345" myhost:8080/singleroute
Run Code Online (Sandbox Code Playgroud)

我想允许并行处理多个不同的 tableName 请求,但同时每个 tableName 只能处理 1 个请求。所以在上面的例子中,如果同时触发了 4 个以上的请求,那么第一个和第三个应该能够同时运行(作为唯一的 tableNames),但是第二个只会在第一个完成后开始,第四个只会在第三个完成后开始。当我研究互斥锁时,似乎没有任何例子适合这种情况,我不想在代码中的任何地方对 abc/def.etc 进行硬编码,因为相同的规则应该适用于任何任意 tableName。

我的猜测基于 Crowman 的帮助:

package main

import (
    "fmt"
    "sync"
    "time"
    "http"
)
km := KeyedMutex{}
type KeyedMutex struct {
    mutexes sync.Map // Zero value is empty and ready for use
}

func (m *KeyedMutex) Lock(key string) func() {
    value, _ := m.mutexes.LoadOrStore(key, &sync.Mutex{})
    mtx := value.(*sync.Mutex)
    mtx.Lock()

    return func() { mtx.Unlock() }
}

func myFunc(key string, data string) string {
  //do some stuff
  return "done for key:"+key+", data: "+data
}

func main() {
    key := //some form value sent to my api 
    data := //some form value sent to my api 
    unlock := km.Lock(key)
    defer unlock()
    retVal := myFunc(key, data)
}
Run Code Online (Sandbox Code Playgroud)

Cro*_*man 7

您可以sync.Map将表名用作键,将 a*sync.Mutex用作值。

例如:

package main

import (
    "fmt"
    "sync"
    "time"
)

type KeyedMutex struct {
    mutexes sync.Map // Zero value is empty and ready for use
}

func (m *KeyedMutex) Lock(key string) func() {
    value, _ := m.mutexes.LoadOrStore(key, &sync.Mutex{})
    mtx := value.(*sync.Mutex)
    mtx.Lock()

    return func() { mtx.Unlock() }
}

func main() {
    wg := sync.WaitGroup{}
    km := KeyedMutex{}

    for _, job := range []struct {
        key  string
        data string
    }{
        {key: "abc", data: "123"},
        {key: "abc", data: "456"},
        {key: "def", data: "123"},
        {key: "def", data: "456"},
    } {
        var job = job
        wg.Add(1)

        go func() {
            defer wg.Done()

            unlock := km.Lock(job.key)
            defer unlock()

            fmt.Printf("%s:%s mutex acquired\n", job.key, job.data)
            time.Sleep(time.Second * 1) // To ensure some goroutines visibly block
            fmt.Printf("%s:%s done\n", job.key, job.data)
        }()
    }

    wg.Wait()
}
Run Code Online (Sandbox Code Playgroud)

带有示例输出:

crow@mac:$ ./mut
def:456 mutex acquired
abc:456 mutex acquired
abc:456 done
def:456 done
abc:123 mutex acquired
def:123 mutex acquired
def:123 done
abc:123 done
crow@mac:$
Run Code Online (Sandbox Code Playgroud)

显示具有不同表名的请求立即获得互斥锁,但具有相同表名的请求被序列化。


Ale*_*nok 0

您可以创建包级别var map[string]*sync.Mutex并锁定将通过表名称获取/创建的相应互斥体。

  • @tooptoop4 您是否有一个不起作用的示例来证明您已尝试过并带有突出显示您面临的问题的评论? (3认同)
  • 请记住,映射的并发规则仍然禁止并发读取或读取和写入。因此,如果您需要在运行时分配新密钥,您还应该使用 [`sync.RWMutex`](https://golang.org/pkg/sync/#RWMutex) 来锁定整个映射 (2认同)
  • 或者只使用sync.Map (2认同)