Concurrency 带通道的读写排除

Concurrency 带通道的读写排除,concurrency,go,Concurrency,Go,我想在Go中写一个小的内存数据库。 读和写请求将通过一个通道传递,并由db引擎处理,这将确保正确完成访问 第一个想法是模仿动物的行为。只有它会使用更惯用的围棋风格 这里有一个小玩具(虽然相当长)的例子,我想做什么 package main import ( "log" "math/rand" "time" ) var source *rand.Rand type ReqType int const ( READ = iota WRITE ) ty

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

第一个想法是模仿动物的行为。只有它会使用更惯用的围棋风格

这里有一个小玩具(虽然相当长)的例子,我想做什么

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))

    blackhole := make(chan *DbResponse, 100)

    d := Db{}
    rc := d.Start(4)
    wc := time.After(3 * time.Second)

    go func() {
        for {
            <-blackhole
        }
    }()

    for {
        select {
        case <-wc:
            return
        default:
            if source.Intn(2) == 0 {
                rc <- &DbRequest{READ, blackhole}
            } else {
                rc <- &DbRequest{WRITE, blackhole}
            }
        }
    }
}
主程序包
进口(
“日志”
“数学/兰德”
“时间”
)
变量源*rand.rand
类型请求类型int
常数(
读取=iota
写
)
类型DbRequest struct{
类型int//请求类型
RespC chan*DbResponse//请求-响应通道
//满足于此
}
类型DbResponse struct{
//回应这里
}
类型数据库结构{
//这里是DB
}
func随机等待(){
time.Sleep(time.Duration(source.Intn(1000))*time.毫秒)
}

func(d*Db)readsHandler(in为什么不直接使用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包对标准输出的写入不是线程安全的,最终会看到乱码输出。请尝试使用日志包。您可以将其设置为写入
对于没有日志前缀的标准输出,它将确保原子写入。

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

谢谢,这就是我所怀疑的。那我就使用RWMutex。顺便问一下,我想知道你指的是什么类型的无锁技术。你有一些关于它的好参考资料吗?最好的参考资料是Dmitry Vyukov的1024核().我是一个初学者,几乎不知道我在做什么,但我确实为Rosetta代码编写了一个示例。请参阅。在两个基于通道的版本之间,一个运行速度比另一个快10倍。RWMutex版本再次运行约50%的速度,然后无锁版本再次运行两倍的速度。比最简单的基于通道的版本提高了300倍!谢谢太多了!我曾经登陆过1024cores.net,我会仔细看看。但是在这种情况下,我将无法同时读取。或者我只是不知道如何读取?不,除非数据库是持久的,所以读取是安全的。