草庐IT

go - 在没有接收方的情况下,是否可以保留数据打开的缓冲 channel ?

coder 2023-06-30 原文

假设一个 channel 有 10 个发送者和一个接收者。发送端函数需要一些时间来返回值。接收者只希望从 channel 中得到一个值(第一个接收到的值),其他 9 个值不用。接收方不需要等待剩下的 9 个值。这就是为什么我没有使用 sync.WaitGroup .

我使用了一个缓冲 channel ,所以当接收方只接收第一个数据时,缓冲 channel 中将有 9 个数据。我的问题是:

  1. 在没有接收方的情况下,是否可以保留数据打开的缓冲 channel ?下面的示例代码是一个简化的代码,但是如果程序是守护进程,它最终会被垃圾回收吗?

  2. 有没有更好的方法来处理这种情况?我尝试使用取消 channel 但失败了。我不确定 context适合这种情况。

示例代码:

package main

import (
    "errors"
    "fmt"
    "math/rand"
    "time"
)

func main() {
    rand.Seed(time.Now().UnixNano())

    i, err := getRandomInt()
    if err != nil {
        fmt.Println(err)
    } else {
        fmt.Println(i)
    }

    fmt.Println("Waiting goroutines to be finished...")
    time.Sleep(2 * time.Second)
}

func getRandomInt() (int, error) {
    ch := make(chan int, 10)

    // 10 senders
    for i := 0; i < 10; i++ {
        go func(i int) {
            defer fmt.Printf("Goroutine #%d finished\n", i)
            fmt.Printf("Goroutine #%d started\n", i)

            data := heavyJob()
            ch <- data
            fmt.Printf("Goroutine #%d sent data %d to ch\n", i, data)
            return
        }(i)
    }

    // 1 receiver
    timeout := time.After(2000 * time.Millisecond)
    for {
        select {
        case value := <-ch:
            // uses only the value received first, the rest are discarded
            return value, nil
        case <-timeout:
            return -1, errors.New("Timeout")
        }
    }
}

// takes 1900~2900ms to finish
func heavyJob() int {
    r := rand.Intn(1000)
    time.Sleep(time.Duration(r+1900) * time.Millisecond)
    return r
}

Run on playground

最佳答案

回答主要问题:

  1. 离开 channel 没问题,会被垃圾回收。
  2. 这似乎是基于个人观点,但对我来说,如果你创建一个有 10 个空格的缓冲 channel 的唯一原因是发送者 goroutines 可以退出;感觉它会从重新设计中受益。还有其他(也许更好)的方法可以确保发送者 goroutines 可以关闭。

本质上,您是在工作人员数量和缓冲 channel 的大小之间创建一个隐式耦合。更改这两个数字中的一个,就会出现死锁/中断! (附带说明一下,缓冲 channel 通常适用于消费者和生产者以相同速率工作但输出不稳定的情况。它是尖峰的,缓冲平滑了波峰和波谷。)

考虑到这一点,我建议最好明确管理您不想要所有值的事实。

这是 getRandomInt() 函数的更新版本。请注意在顶部使用 defer 的上下文取消设置,以及在发送时使用 select 语句。

func getRandomInt() (int, error) {
    ctx := context.Background() // creates a fresh, empty context
    ctx, cancel := context.WithCancel(ctx)
    defer cancel() // cancels the context when getRandomInt() returns

    ch := make(chan int)

    // 10 senders
    for i := 0; i < 10; i++ {
        go func(i int) {
            defer fmt.Printf("Goroutine #%d finished\n", i)
            fmt.Printf("Goroutine #%d started\n", i)

            data := heavyJob()

            // this select statement wil block until either this goroutine 
            // is the first to send, or the context is cancelled. In which case
            // another routine has already sent and it can discard it's values.
            select { 
            case ch <- data:
                fmt.Printf("Goroutine #%d sent data %d to ch\n", i, data)
            case <-ctx.Done():
                fmt.Printf("Goroutine #%d did not send, context is cancelled, would have sent data %d to ch\n", i, data)
            }
        }(i)
    }

    // 1 receiver
    timeout := time.After(2000 * time.Millisecond)
    select {
    case value := <-ch:
        // uses only the value received first, the rest are discarded
        return value, nil
    case <-timeout:
        return -1, errors.New("Timeout")
    }
}

设置带有取消的上下文意味着一旦调用 cancel() 函数,上下文就会变为“完成”。这是一种告诉所有发送者 goroutines 不要费心等待发送的方法。

发送时,select 语句阻塞,直到上下文被 cancel() 函数取消;或者接收方方法读取第一个值。

我还从 channel 中删除了缓冲,因为不再需要它了。

关于go - 在没有接收方的情况下,是否可以保留数据打开的缓冲 channel ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50616147/

有关go - 在没有接收方的情况下,是否可以保留数据打开的缓冲 channel ?的更多相关文章

  1. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  2. ruby - 难道Lua没有和Ruby的method_missing相媲美的东西吗? - 2

    我好像记得Lua有类似Ruby的method_missing的东西。还是我记错了? 最佳答案 表的metatable的__index和__newindex可以用于与Ruby的method_missing相同的效果。 关于ruby-难道Lua没有和Ruby的method_missing相媲美的东西吗?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/7732154/

  3. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

  4. ruby - 我可以使用 Ruby 从 CSV 中删除列吗? - 2

    查看Ruby的CSV库的文档,我非常确定这是可能且简单的。我只需要使用Ruby删除CSV文件的前三列,但我没有成功运行它。 最佳答案 csv_table=CSV.read(file_path_in,:headers=>true)csv_table.delete("header_name")csv_table.to_csv#=>ThenewCSVinstringformat检查CSV::Table文档:http://ruby-doc.org/stdlib-1.9.2/libdoc/csv/rdoc/CSV/Table.html

  5. ruby-on-rails - rails 目前在重启后没有安装 - 2

    我有一个奇怪的问题:我在rvm上安装了ruby​​onrails。一切正常,我可以创建项目。但是在我输入“railsnew”时重新启动后,我有“程序'rails'当前未安装。”。SystemUbuntu12.04ruby-v"1.9.3p194"gemlistactionmailer(3.2.5)actionpack(3.2.5)activemodel(3.2.5)activerecord(3.2.5)activeresource(3.2.5)activesupport(3.2.5)arel(3.0.2)builder(3.0.0)bundler(1.1.4)coffee-rails(

  6. ruby - 默认情况下使选项为 false - 2

    这是在Ruby中设置默认值的常用方法:classQuietByDefaultdefinitialize(opts={})@verbose=opts[:verbose]endend这是一个容易落入的陷阱:classVerboseNoMatterWhatdefinitialize(opts={})@verbose=opts[:verbose]||trueendend正确的做法是:classVerboseByDefaultdefinitialize(opts={})@verbose=opts.include?(:verbose)?opts[:verbose]:trueendend编写Verb

  7. ruby - 在没有 sass 引擎的情况下使用 sass 颜色函数 - 2

    我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re

  8. ruby - 我可以使用 aws-sdk-ruby 在 AWS S3 上使用事务性文件删除/上传吗? - 2

    我发现ActiveRecord::Base.transaction在复杂方法中非常有效。我想知道是否可以在如下事务中从AWSS3上传/删除文件:S3Object.transactiondo#writeintofiles#raiseanexceptionend引发异常后,每个操作都应在S3上回滚。S3Object这可能吗?? 最佳答案 虽然S3API具有批量删除功能,但它不支持事务,因为每个删除操作都可以独立于其他操作成功/失败。该API不提供任何批量上传功能(通过PUT或POST),因此每个上传操作都是通过一个独立的API调用完成的

  9. ruby - 有人可以帮助解释类创建的 post_initialize 回调吗 (Sandi Metz) - 2

    我正在阅读SandiMetz的POODR,并且遇到了一个我不太了解的编码原则。这是代码:classBicycleattr_reader:size,:chain,:tire_sizedefinitialize(args={})@size=args[:size]||1@chain=args[:chain]||2@tire_size=args[:tire_size]||3post_initialize(args)endendclassMountainBike此代码将为其各自的属性输出1,2,3,4,5。我不明白的是查找方法。当一辆山地自行车被实例化时,因为它没有自己的initialize方法

  10. ruby - 是否可以覆盖 gemfile 进行本地开发? - 2

    我们的git存储库中目前有一个Gemfile。但是,有一个gem我只在我的环境中本地使用(我的团队不使用它)。为了使用它,我必须将它添加到我们的Gemfile中,但每次我checkout到我们的master/dev主分支时,由于与跟踪的gemfile冲突,我必须删除它。我想要的是类似Gemfile.local的东西,它将继承从Gemfile导入的gems,但也允许在那里导入新的gems以供使用只有我的机器。此文件将在.gitignore中被忽略。这可能吗? 最佳答案 设置BUNDLE_GEMFILE环境变量:BUNDLE_GEMFI

随机推荐