Go实现阻塞读且并发安全的map

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存在或者超时
}

解析:

看到阻塞协程第一个想到的就是channel,题目中要求并发安全,那么必须用锁,还要实现多个goroutine读的时候如果值不存在则阻塞,直到写入值,那么每个键值需要有一个阻塞goroutinechannel

实现如下:

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
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值