通道的读写排斥

我想在Go中编写一个小的内存数据库。读写请求将通过通道传递并由db引擎处理,这将确保正确完成访问。


第一个想法是模仿RWMutex的行为。只有它会使用更惯用的go风格。


这是我想做的一个小玩具(虽然很长)的例子。


package main


import (

    "log"

    "math/rand"

    "time"

)


var source *rand.Rand


type ReqType int


const (

    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和通道的巧妙解决方案。


qq_花开花谢_0
浏览 194回答 2
2回答

ibeautiful

为什么不只使用RWMutex?它已经过优化,非常高效,并且在概念上很简单。只需在您的Db对象中嵌入一个type Db struct {&nbsp; &nbsp; sync.RWMutex&nbsp; &nbsp; // DB here}你可以这样称呼它db := &Db{}...db.Lock()// do RW operationsdb.Unlock()...db.RLock()// do Read operationsdb.RUnlock()我不知道一种使用渠道获得更好性能的方法。但是,您可以使用无锁技术获得更好的性能,但是我建议您先运行RWMutex版本。另一个并发问题是,将fmt包写入stdout并不是线程安全的,最终您将看到乱码。请尝试使用日志包。您可以将其设置为在没有日志记录前缀的情况下写入stdout,这将确保原子写入。

紫衣仙女

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

相关分类

Go