首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Go竞赛检测器错误

Go竞赛检测器错误
EN

Stack Overflow用户
提问于 2018-05-08 03:57:52
回答 1查看 100关注 0票数 0

下面是并发缓存的代码:

代码语言:javascript
复制
package cache

import (
    "sync"
)

// Func represents a memoizable function, operating on a string key, to use with a Cache
type Func func(key string) (interface{}, error)

// FuncResult stores the value of a Func call
type FuncResult struct {
    val interface{}
    err error
}

// Cache is a cache that memoizes results of an expensive computation
//
// It has a traditional implementation using mutexes.
type Cache struct {
    // guards done
    mu   sync.RWMutex
    done map[string]chan bool
    memo map[string]*FuncResult
    f    Func
}

// New creates a new Cache and returns its pointer
func New(f Func) *Cache {
    return &Cache{
        memo: make(map[string]*FuncResult),
        done: make(map[string]chan bool),
        f:    f,
    }
}

// Get a string key if it exists, otherwise computes the value and caches it.
//
// Returns the value and whether or not the key existed.
func (c *Cache) Get(key string) (*FuncResult, bool) {
    c.mu.RLock()
    _, ok := c.done[key]
    c.mu.RUnlock()
    if ok {
        return c.get(key), true
    }

    c.mu.Lock()
    _, ok = c.done[key]
    if ok {
        c.mu.Unlock()
    } else {
        c.done[key] = make(chan bool)
        c.mu.Unlock()

        v, err := c.f(key)
        c.memo[key] = &FuncResult{v, err}

        close(c.done[key])
    }
    return c.get(key), ok
}

// get returns the value of key, blocking on an existing computation
func (c *Cache) get(key string) *FuncResult {
    <-c.done[key]
    fresult, _ := c.memo[key]
    return fresult
}

当我使用竞赛检测器运行这个程序时,我没有发现错误:

代码语言:javascript
复制
package main

import (
    "fmt"
    "log"
    "sync"
    "time"

    "github.com/yangmillstheory/go-cache/cache"
)

var f = func(key string) (interface{}, error) {
    log.Printf("Computing value for key %s\n", key)
    time.Sleep(1000 * time.Millisecond)
    return fmt.Sprintf("value for %s", key), nil
}

func main() {
    var wg sync.WaitGroup

    c := cache.New(f)
    n := 10
    k := "key1"

    start := time.Now()
    for i := 0; i < n; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            c.Get(k)
        }()
    }

    wg.Wait()
    log.Printf("Elapsed: %s\n", time.Since(start))
}

但是,当我在循环体中启动两个不同的goroutine(每个都得到不同的键)时,我会得到一个错误:

解决这个问题的方法是添加另一个互斥c.nu来保护memo,但是它会使程序变得更慢、更复杂。

代码语言:javascript
复制
func (c *Cache) Get(key string) (*FuncResult, bool) {
    c.mu.RLock()
    _, ok := c.done[key]
    c.mu.RUnlock()
    if ok {
        return c.get(key), true
    }

    c.mu.Lock()
    _, ok = c.done[key]
    if ok {
        c.mu.Unlock()
    } else {
        c.done[key] = make(chan bool)
        c.mu.Unlock()

        v, err := c.f(key)
        c.nu.Lock()
        c.memo[key] = &FuncResult{v, err}
        c.nu.Unlock()

        close(c.done[key])
    }
    return c.get(key), ok
}

// get returns the value of key, blocking on an existing computation
func (c *Cache) get(key string) *FuncResult {
    <-c.done[key]
    c.nu.RLock()
    fresult, _ := c.memo[key]
    c.nu.RUnlock()
    return fresult
}

这里真的有什么比赛条件需要担心吗?如果不同的goroutines同时访问同一数据结构中的不同密钥,那么只要在对给定密钥的访问中发生同步,就不应该成为一个问题吗?

换句话说,您必须跨所有键进行同步,还是只跨同一键进行同步?并发备忘录的用例似乎表明后者就足够了?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-05-08 04:44:36

映射需要同步,特别是您的写的时候看不懂,不管它是相同的还是不同的键,所以您需要锁定菜单映射。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/50225601

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档