Concurrency 带通道的读写排除
我想在Go中写一个小的内存数据库。 读和写请求将通过一个通道传递,并由db引擎处理,这将确保正确完成访问 第一个想法是模仿动物的行为。只有它会使用更惯用的围棋风格 这里有一个小玩具(虽然相当长)的例子,我想做什么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
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,我会仔细看看。但是在这种情况下,我将无法同时读取。或者我只是不知道如何读取?不,除非数据库是持久的,所以读取是安全的。