草庐IT

concurrency - channel 的读写排除

coder 2023-06-26 原文

我想用 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/

有关concurrency - channel 的读写排除的更多相关文章

  1. ruby - 正则表达式 - 排除一个字符 - 2

    这是一个例子:s="abcd+subtext@example.com"s.match(/+[^@]*/)Result=>"+subtext"问题是,我不想在其中包含“+”。我希望结果是“潜台词”,没有+ 最佳答案 您可以在正则表达式中使用括号来创建匹配组:s="abcd+subtext@example.com"s=~/\+([^@]*)/&&$1=>"subtext" 关于ruby-正则表达式-排除一个字符,我们在StackOverflow上找到一个类似的问题:

  2. ruby - 如何排除无效日期 ruby - 2

    我想知道我应该引用什么异常名称。我的日期无效。我检查了文档,但找不到。BeginDate.new(day,month,year)Rescueexceptionnamestatements 最佳答案 我认为您正在寻找ArgumentError.使用irb:>Date.new(2,-200,3)ArgumentError:invaliddatefrom(irb):11:in`new'from(irb):11所以beginDate.new(2,-200,3)rescueArgumentError#yourlogicend

  3. ruby-on-rails - Rails add_index 算法 : :concurrently still causes database lock up during migration - 2

    为了防止在迁移到生产站点期间出现数据库事务错误,我们遵循了https://github.com/LendingHome/zero_downtime_migrations中列出的建议。(具体由https://robots.thoughtbot.com/how-to-create-postgres-indexes-concurrently-in概述),但在特别大的表上创建索引期间,即使是索引创建的“并发”方法也会锁定表并导致该表上的任何ActiveRecord创建或更新导致各自的事务失败有PG::InFailedSqlTransaction异常。下面是我们运行Rails4.2(使用Acti

  4. ruby - 在未安装 Excel 的服务器上使用 Ruby 读写 Excel 文件 - 2

    我需要在一台没有安装Excel的Linux服务器上读写(->转换)Excel文件。对于Python,存在http://www.python-excel.org/.Ruby有类似的东西吗?可能不需要处理最新的Office格式。只需旧的xls文件就足够了。 最佳答案 我同意Gonzih的观点,并且我经常使用roo。它允许我使用模板文件进行读取、写入和写入。该项目在他们的site上有很好的记录。.我总是使用类似的东西:input=Excel.new(path)output=Array.newinput.default_sheet=inpu

  5. ruby - 使用符号读写 Sinatra 参数,例如参数[:id] - 2

    我的表单通过POST接收数据。当我执行putsparams时,我可以看到:{"id"=>"123","id2"=>"456"}现在是命令:putsparams['id']#=>123putsparams[:id]#=>123params['id']='999'putsparams#=>{"id"=>"999","id2"=>"456"}但是当我这样做的时候:params[:id]='888'putsparams我明白了{"id"=>"999","id2"=>"456",:id=>"888"}在IRB中它工作正常:params#=>{"id2"=>"2","id"=>"1"}params

  6. ruby-on-rails - 为 UrlFormat 添加排除选项到 scss_lint gem - 2

    我正在使用gemscss_lint进行scsslinting。我遇到的问题是我在url的css中的一些字符串中有变量,所以linter报告错误:我认为应该有一种方法可以在检查中排除某些东西。我看到data:已被列入白名单,但我们有时会在我们的css中使用变量,因为它被放入了cms。UrlFormat:InvalidURL'/response/${siteId}/artifact/${campaignAndToolPath}/assets/fonts/roboto-light-webfont.woff2':badURI(isnotURI?):/response/${siteId}/art

  7. AT24C04、AT24C08、AT24C16系列EEPROM芯片单片机读写驱动程序 - 2

    一、概述在之前的一篇博文中,记录了AT24C01、AT24C02芯片的读写驱动,先将之前的相关文章include一下:1.IIC驱动:4位数码管显示模块TM1637芯片C语言驱动程序2.AT24C01/AT24C02读写:AT24C01/AT24C02系列EEPROM芯片单片机读写驱动程序本文记录分享AT24C04、AT24C08、AT24C16芯片的单片机C语言读写驱动程序。二、芯片对比介绍型号容量bit容量byte页数字节/页器件寻址位可寻址器件数WordAddress位数/字节数备注AT24C044k5123216A2A149/1WordAddress使用P0位AT24C088k1024

  8. ruby - 如何执行 Dir.glob 但排除目录? - 2

    如果我想获取所有的CSS和JavaScript文件Dir.glob("dir/**/*.{css,js})如果有一个名为stupidfolder.js的文件夹,它会给我一些我不想要的东西。我只想更改文件夹的名称,但我不能。 最佳答案 你不能用Dir.glob做到这一点。您必须明确拒绝这些条目。only_files=Dir.glob('*').rejectdo|path|File.directory?(path)end 关于ruby-如何执行Dir.glob但排除目录?,我们在StackO

  9. ruby-on-rails - 如何在 Rails Controller 中调用 channel 方法? - 2

    我有一个订阅用户的ActionCable方法。如果开始新的session,我也想为用户订阅新channel。我想不出在Controller中调用channel方法的正确语法。更新:问题是消息在发送时附加到聊天框,但是当发送第一条消息时,websocket连接尚未建立,因此在用户看来好像消息没有发送(因为它没有被附加)。channel/msgs_channel.rbclassMsgsChannel在我的convosController中,create方法,我尝试了几种方法:convos_controller.rbdefcreate@convo=Convo.create!({sender_

  10. ruby - 我可以使用 RSpec 模拟标准输入/标准输出来测试控制台读写吗? - 2

    我的Ruby程序从stdin读取行并使用puts打印到stdout(终端)。我可以使用RSpec来测试读写吗?我可以像在stdin中编写的那样向我的程序注入(inject)一个字符串,同时检查输出吗?line=STDIN.read.chomp.split另外,我在一个循环中进行读取和写入,直到line[0]被“退出”。我可以在循环运行时进行测试,还是应该调用subject.read_in和subject.write_out? 最佳答案 您可以使用模拟并通过在and_return()方法中列出多个值来多次调用该方法。这些将按照给定的顺

随机推荐