gpt4 book ai didi

concurrency - channel 的读写排除

转载 作者:IT王子 更新时间:2023-10-29 01:26:57 26 4
gpt4 key购买 nike

我想用 Go 编写一个小型内存数据库。读取和写入请求将通过 channel 传递并由数据库引擎处理,这将确保正确完成访问。

第一个想法是模仿 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))

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

当然,这个例子说明了读/写冲突。

我觉得我正在尝试做一些有点邪恶的事情:使用旨在避免它的结构共享内存......在这一点上,一个明显的解决方案是围绕两种类型的请求处理添加 RWMutex 锁,但也许有一个仅使用 goroutines 和 channel 的聪明解决方案。

最佳答案

为什么不直接使用 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()

我不知道如何使用 channel 获得更好的性能。不过,您可以使用无锁技术获得更好的性能,但我建议先运行您的 RWMutex 版本。

另一个并发问题是 fmt 包写入 stdout 不是线程安全的,您最终会看到乱码输出。请尝试使用 log 包。你可以设置它写到没有日志记录前缀的标准输出,它将确保原子写入。

关于concurrency - channel 的读写排除,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14050973/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com