GO里面MAP如何实现key不存在 get操作等待 直到key存在或者超时,保证并发安全

保证并发安全,实现如下接口

type sp interface {
    Out(key string, val interface{})  //存入key /val,如果该key读取的goroutine挂起,则唤醒。此方法不会阻塞,时刻都可以立即执行并返回
    Rd(key string, timeout time.Duration) interface{}  //读取一个key,如果key不存在阻塞,等待key存在或者超时
}
阅读 6k
2 个回答

可以利用channel关闭goroutine不阻塞特性来实现:

下面的代码可以实现你的需求,只是没有写key被多次写入的判断逻辑。

type sp interface {
    Out(key string, val interface{})                  //存入key /val,如果该key读取的goroutine挂起,则唤醒。此方法不会阻塞,时刻都可以立即执行并返回
    Rd(key string, timeout time.Duration) interface{} //读取一个key,如果key不存在阻塞,等待key存在或者超时
}

type Map struct {
    c   map[string]*entry
    rmx *sync.RWMutex
}
type entry struct {
    ch      chan struct{}
    value   interface{}
    isExist bool
}

func (m *Map) Out(key string, val interface{}) {
    m.rmx.Lock()
    defer m.rmx.Unlock()
    if e, ok := m.c[key]; ok {
        e.value = val
        e.isExist = true
        close(e.ch)
    } else {
        e = &entry{ch: make(chan struct{}), isExist: true,value:val}
        m.c[key] = e
        close(e.ch)
    }
}

func (m *Map) Rd(key string, timeout time.Duration) interface{} {
    m.rmx.Lock()
    if e, ok := m.c[key]; ok && e.isExist {
        m.rmx.Unlock()
        return e.value
    } else if !ok {
        e = &entry{ch: make(chan struct{}), isExist: false}
        m.c[key] = e
        m.rmx.Unlock()
        fmt.Println("协程阻塞 -> ", key)
        select {
        case <-e.ch:
            return e.value
        case <-time.After(timeout):
            fmt.Println("协程超时 -> ", key)
            return nil
        }
    } else {
        m.rmx.Unlock()
        fmt.Println("协程阻塞 -> ", key)
        select {
        case <-e.ch:
            return e.value
        case <-time.After(timeout):
            fmt.Println("协程超时 -> ", key)
            return nil
        }
    }
}

写了一段代码,不知道是否符合你的要求:

func main() {
    m := make(map[string]int)
    keyChan := make(chan string)
    valChan := make(chan int)
    var wg sync.WaitGroup
    var l sync.RWMutex
    done := make(chan struct{})

    for i := 0; i < 3; i++ {
        go func(i int) {
            key := "test" + strconv.Itoa(i)
            l.RLock()
            v, ok := m[key]
            l.RUnlock()
            if !ok {
                wg.Add(1)
                go func() {
                    keyChan <- key
                    defer wg.Done()
                    timeout := time.After(10 * time.Second)
                    select {
                    case <-timeout:
                        log.Panic("wait too long!")
                    case v := <-valChan:
                        log.Printf("val is %d\n", v)
                    }
                }()
                wg.Wait()
            } else {
                log.Printf("val is %d\n", v)
            }
            done <- struct{}{}
        }(i)
    }

    go func() {
        rand.Seed(time.Now().Unix())
        for {
            select {
            case k := <-keyChan:
                time.Sleep(2 * time.Second)
                l.Lock()
                v := rand.Intn(100)
                m[k] = v
                l.Unlock()
                valChan <- v
            default:

            }
        }
    }()

    select {}
}
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题