我可以在Go中使用特定值锁定吗? [英] Can I lock using specific values in Go?

查看:66
本文介绍了我可以在Go中使用特定值锁定吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在回答另一个问题时,我使用

In answering another question I wrote a little struct using sync.Map to cache API requests.

type PostManager struct {
    sync.Map
}

func (pc PostManager) Fetch(id int) Post {
    post, ok := pc.Load(id)
    if ok {
        fmt.Printf("Using cached post %v\n", id)
        return post.(Post)
    }
    fmt.Printf("Fetching post %v\n", id)
    post = pc.fetchPost(id)
    pc.Store(id, post)

    return post.(Post)
}

不幸的是,如果两个goroutine都同时获取同一未缓存的Post,则它们都会发出请求.

Unfortunately, if two goroutines both fetch the same uncached Post at the same time, both will make a request.

var postManager PostManager

wg.Add(3)

var firstPost Post
var secondPost Post
var secondPostAgain Post

go func() {
    // Fetches and caches 1
    firstPost = postManager.Fetch(1)
    defer wg.Done()
}()

go func() {
    // Fetches and caches 2
    secondPost = postManager.Fetch(2)
    defer wg.Done()
}()

go func() {
    // Also fetches and caches 2
    secondPostAgain = postManager.Fetch(2)
    defer wg.Done()
}()

wg.Wait()

我需要确保在同时提取相同ID的情况下,仅允许一个人实际发出请求.另一个必须等​​待,并将使用缓存的Post.但是也不能锁定对不同ID的提取.

I need to ensure when there are simultaneous fetches of the same ID only one is allowed to actually make the request. The other must wait and will use the cached Post. But to also not lock out fetches of different IDs.

在上面的示例中,我希望只有一个调用 pc.fetchPost(1) pc.fetchPost(2),它们应该是同时进行.

In the above example, I want there to be one and only one call to pc.fetchPost(1) and pc.fetchPost(2) and they should be simultaneous.

链接到完整代码.

推荐答案

golang.org/x/sync/singleflight软件包正是为此目的而编写的.

The golang.org/x/sync/singleflight package has been written for exactly this purpose.

请注意,所有缓存访问都应该在传递给Do的回调函数内进行.在您的注释中链接的代码中,您可以进行外部查找;这有点违背了目的.

Note that all cache accesses are supposed to happen inside the callback function passed to Do. In the code you link to in your comment you do the lookup outside; that somewhat defeats the purpose.

此外,您必须使用指向singleflight.Group的指针.这就是您的数据竞赛的源泉,而兽医指出了这一点:

Also, you must use a pointer to singleflight.Group. That's the source for your data race and go vet points it out:

./foo.go:41:10:fetchPost通过值传递锁:命令行参数.PostManager包含golang.org/x/sync/singleflight.Group包含sync.Mutex

./foo.go:41:10: fetchPost passes lock by value: command-line-arguments.PostManager contains golang.org/x/sync/singleflight.Group contains sync.Mutex

这是我的写法(在操场上的完整示例: https://play.golang.org/p/2hE721uA88S ):

Here's how I would write it (full example on the playground: https://play.golang.org/p/2hE721uA88S):

import (
    "strconv"
    "sync"

    "golang.org/x/sync/singleflight"
)

type PostManager struct {
    sf    *singleflight.Group
    cache *sync.Map
}

func (pc *PostManager) Fetch(id int) Post {
    x, _, _ := pc.sf.Do(strconv.Itoa(id), func() (interface{}, error) {
        post, ok := pc.cache.Load(id)
        if !ok {
            post = pc.fetchPost(id)
            pc.cache.Store(id, post)
        }

        return post, nil
    })

    return x.(Post)
}

这篇关于我可以在Go中使用特定值锁定吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆