为了账号安全,请及时绑定邮箱和手机立即绑定

通道的读写排斥

通道的读写排斥

Go
qq_花开花谢_0 2021-05-15 11:10:45
我想在Go中编写一个小的内存数据库。读写请求将通过通道传递并由db引擎处理,这将确保正确完成访问。第一个想法是模仿RWMutex的行为。只有它会使用更惯用的go风格。这是我想做的一个小玩具(虽然很长)的例子。package mainimport (    "log"    "math/rand"    "time")var source *rand.Randtype ReqType intconst (    READ = iota    WRITE)type DbRequest struct {    Type  int              // request type    RespC chan *DbResponse // channel for request response    // content here}type DbResponse struct {    // response here}type Db struct {    // DB here}func randomWait() {    time.Sleep(time.Duration(source.Intn(1000)) * time.Millisecond)}func (d *Db) readsHandler(in <-chan *DbRequest) {    for r := range in {        id := source.Intn(4000000)        log.Println("read ", id, " starts")        randomWait()        log.Println("read ", id, " ends")        r.RespC <- &DbResponse{}    }}func (d *Db) writesHandler(r *DbRequest) *DbResponse {    id := source.Intn(4000000)    log.Println("write ", id, " starts")    randomWait()    log.Println("write ", id, " ends")    return &DbResponse{}}func (d *Db) Start(nReaders int) chan *DbRequest {    in := make(chan *DbRequest, 100)    reads := make(chan *DbRequest, nReaders)    // launch readers    for k := 0; k < nReaders; k++ {        go d.readsHandler(reads)    }    go func() {        for r := range in {            switch r.Type {            case READ:                reads <- r            case WRITE:                // here we should wait for all reads to                // be over (how ??)                r.RespC <- d.writesHandler(r)                // here writesHandler is blocking,                // this ensures that no additional                // read is added in the reads channel                // before the write is finished            }        }    }()    return in}func main() {    seed := time.Now().Unix()    source = rand.New(rand.NewSource(seed))当然,此示例显示了读/写冲突。我觉得我正在尝试做一些邪恶的事情:使用旨在避免这种情况的结构共享内存...此时,一个显而易见的解决方案是在两种请求处理类型周围添加RWMutex锁,但也许仅使用goroutine和通道的巧妙解决方案。
查看完整描述

2 回答

?
ibeautiful

TA贡献1993条经验 获得超5个赞

为什么不只使用RWMutex?它已经过优化,非常高效,并且在概念上很简单。只需在您的Db对象中嵌入一个


type Db struct {

    sync.RWMutex

    // DB here

}

你可以这样称呼它


db := &Db{}

...

db.Lock()

// do RW operations

db.Unlock()

...

db.RLock()

// do Read operations

db.RUnlock()

我不知道一种使用渠道获得更好性能的方法。但是,您可以使用无锁技术获得更好的性能,但是我建议您先运行RWMutex版本。


另一个并发问题是,将fmt包写入stdout并不是线程安全的,最终您将看到乱码。请尝试使用日志包。您可以将其设置为在没有日志记录前缀的情况下写入stdout,这将确保原子写入。


查看完整回答
反对 回复 2021-05-24
?
紫衣仙女

TA贡献1839条经验 获得超15个赞

另一种可能的解决方案是将数据库本身通过通道传递,然后仅在您拥有数据库时才对其进行更新。这意味着您不需要对其进行锁定,因为只有持有者可以对其进行写操作,并且内存模型可以保证对数据库IIRC的写操作。


查看完整回答
反对 回复 2021-05-24
  • 2 回答
  • 0 关注
  • 193 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信